diff options
Diffstat (limited to 'usr/src/lib')
637 files changed, 96789 insertions, 5502 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 374c46d532..9fd28c40f6 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -97,6 +97,7 @@ SUBDIRS += \ libbrand \ libbsdmalloc \ libbsm \ + libbunyan \ libc_db \ libcfgadm \ libcmd \ @@ -142,6 +143,7 @@ SUBDIRS += \ libgss \ libhotplug \ libidmap \ + libidspace \ libilb \ libima \ libinetsvc \ @@ -192,9 +194,12 @@ SUBDIRS += \ libpthread \ libraidcfg \ librcm \ + librefhash \ + librename \ libreparse \ libresolv \ libresolv2 \ + libresolv2_joy \ librestart \ librpcsvc \ librsm \ @@ -213,6 +218,7 @@ SUBDIRS += \ libsip \ libsldap \ libslp \ + libsmartsshd \ libsmbfs \ libsmbios \ libsmedia \ @@ -236,6 +242,7 @@ SUBDIRS += \ libutempter \ libuuid \ libuutil \ + libvnd \ libvolmgt \ libvrrpadm \ libvscan \ @@ -244,6 +251,7 @@ SUBDIRS += \ libxcurses \ libxcurses2 \ libxnet \ + libzdoor \ libzfs \ libzfs_core \ libzfs_jni \ @@ -263,9 +271,6 @@ SUBDIRS += \ pkcs11 \ policykit \ print \ - pylibbe \ - pysolaris \ - pyzfs \ raidcfg_plugins \ rpcsec_gss \ sasl_plugins \ @@ -276,6 +281,7 @@ SUBDIRS += \ sun_fc \ sun_sas \ udapl \ + varpd \ watchmalloc \ $($(MACH)_SUBDIRS) @@ -350,6 +356,7 @@ MSGSUBDIRS= \ libshell \ libsldap \ libslp \ + libsmartsshd \ libsmbfs \ libsmedia \ libsum \ @@ -359,10 +366,10 @@ MSGSUBDIRS= \ libvscan \ libzfs \ libzonecfg \ + libzdoor \ madv \ mpss \ pam_modules \ - pyzfs \ rpcsec_gss \ $($(MACH)_MSGSUBDIRS) @@ -382,6 +389,7 @@ HDRSUBDIRS= \ libast \ libbrand \ libbsm \ + libbunyan \ libc \ libcmd \ libcmdutils \ @@ -415,6 +423,7 @@ HDRSUBDIRS= \ libgen \ libgrubmgmt \ libidmap \ + libidspace \ libilb \ libima \ libinetsvc \ @@ -452,6 +461,7 @@ HDRSUBDIRS= \ libproc \ libraidcfg \ librcm \ + librename \ libreparse \ librestart \ librpcsvc \ @@ -486,6 +496,7 @@ HDRSUBDIRS= \ libumem \ libutempter \ libuutil \ + libvnd \ libvolmgt \ libvrrpadm \ libvscan \ @@ -504,6 +515,7 @@ HDRSUBDIRS= \ smbsrv \ smhba \ udapl \ + varpd \ $($(MACH)_HDRSUBDIRS) i386_HDRSUBDIRS= \ @@ -587,7 +599,7 @@ dbusdeps: libsecdb libtsol libinetutil libscf libuutil libgen libsmbios ../cmd/sendmail/libmilter: libsocket libnsl abi: libctf libmapmalloc libproc auditd_plugins: libbsm libsecdb libgss libmtmalloc -brand: libzonecfg libmapmalloc +brand: libzonecfg libmapmalloc libipadm libcustr libproc librpcsvc cfgadm_plugins: libdevice libdevinfo libhotplug librcm hbaapi libkstat libscf fm: libexacct libipmi libzfs scsi libdevinfo libdevid libcfgadm \ libcontract libsysevent ../cmd/sgs/libelf libdladm libsff \ @@ -604,6 +616,7 @@ libadt_jni: libbsm libadutils: libldap5 libresolv2 libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl libbsm: libinetutil libscf libsecdb libtsol +libbunyan: libumem libcfgadm: libdevinfo libcontract: libnvpair libcmd: libsum libast @@ -620,11 +633,11 @@ libdhcputil: libgen libinetutil libdlpi libdiskmgt: libdevid libdevinfo libadm libefi libkstat libsysevent $(INTEL_BLD)libdiskmgt: libfdisk libdladm: libdevinfo libinetutil libscf librcm libexacct libkstat \ - libpool + libpool varpd libdll: libast libdlpi: libinetutil libdladm libds: libsysevent -libdtrace: libproc libgen libctf libmapmalloc +libdtrace: libproc libgen libctf libmapmalloc libzonecfg ../cmd/sgs/libelf libdtrace_jni: libuutil libdtrace libdwarf: ../cmd/sgs/libelf libefi: libuuid libsmbios @@ -639,6 +652,7 @@ libfsmgt: libkstat libgrubmgmt: libdevinfo libzfs libfstyp libefi $(INTEL_BLD)libgrubmgmt: libfdisk libidmap: libavl libuutil +libidspace: libumem libinetsvc: libscf libinetutil: libsocket libinstzones: libzonecfg libcontract @@ -669,6 +683,7 @@ libprtdiag: libkstat libdevinfo libprtdiag_psr: libprtdiag libraidcfg: libdevinfo librcm: libnvpair +librefhash: libumem librestart: libuutil libscf libpool libproject libsecdb libsysevent libsasl: libgss pkcs11 libsaveargs: libdisasm @@ -680,6 +695,7 @@ libshare: libscf libzfs libuuid libfsmgt libsecdb libumem libsmbfs libshell: libast libcmd libdll libsecdb libsip: libmd5 libsldap: libldap5 libscf +libsmartsshd: libc libcontract libsmbfs: libkrb5 libsec libidmap pkcs11 libsmbios: libdevinfo libsrpt: libstmf @@ -700,6 +716,7 @@ libvmm: libvmmapi libvolmgt: libadm libvrrpadm: libdladm libscf libvscan: libscf libsecdb +libzdoor: libc libzonecfg libcontract libzfs: libdevid libgen libuutil libavl libefi libidmap \ libumem libtsol libzfs_core libcryptoutil pkcs11 libmd libzutil libzfs_core: libnvpair @@ -712,7 +729,8 @@ libzutil: libadm libavl libdevid libefi madv: libgen mpapi: libpthread libdevinfo libsysevent mpss: libgen -nsswitch: libadutils libidmap libdns_sd libscf libldap5 libsldap +nsswitch: libadutils libidmap libdns_sd libscf libldap5 libsldap \ + libresolv2_joy pam_modules: libproject passwdutil smbsrv libtsnet libpam libbsm libsecdb passwdutil: libsldap pkcs11: libcryptoutil libgen libuuid @@ -733,6 +751,8 @@ storage: libdevice libdevinfo libdevid sun_fc: libdevinfo libsysevent sun_sas: libdevinfo libsysevent libkstat libdevid udapl: libdevinfo libdladm +varpd: libavl libidspace libumem libnsl libnvpair libmd5 librename \ + libbunyan libcustr # # The reason this rule checks for the existence of the diff --git a/usr/src/lib/Makefile.astmsg b/usr/src/lib/Makefile.astmsg index 096805e825..ff8202d03a 100644 --- a/usr/src/lib/Makefile.astmsg +++ b/usr/src/lib/Makefile.astmsg @@ -47,8 +47,8 @@ MSGLIBNAME= $(LIBRARY:.a=) ASTMSGCATALOG= $(ROOT)/usr/lib/locale/C/LC_MESSAGES/$(MSGLIBNAME) $(DO_BUILD_AST_CATALOGS)ASTMSGCC= \ - PATH="/usr/ast/bin/:/bin:/usr/bin" \ - /usr/bin/ksh93 /usr/ast/bin/msgcc >>msgcc.out 2>&1 + PATH="$(ASTBINDIR):/bin:/usr/bin" \ + /usr/bin/ksh93 $(MSGCC) >>msgcc.out 2>&1 ASTMSGS= $(OBJECTS:%.o=msgs/%.mso) diff --git a/usr/src/lib/Makefile.lib b/usr/src/lib/Makefile.lib index b87923b006..83df083ce3 100644 --- a/usr/src/lib/Makefile.lib +++ b/usr/src/lib/Makefile.lib @@ -124,6 +124,25 @@ SONAME= $(DYNLIB) # either within the library or as dependencies, all text should be pure, and # combining relocations into one relocation table reduces startup costs. # All options are tunable to allow overload/omission from lower makefiles. + +# +# DTrace related rules +# +# These allow for multiple USDT providers to be specified by a library. +# If a library needs to break up the set of objects that are passed to +# the dtrace -G invocation, then they can place the following in heir +# Makefile.com: +# +# pics/<provider>.o := USDT_G_PICS = <files> +# +# <provider> should be replaced with the basename of one of the USDT +# providers specified in USDT_PROVIDERS in their Makefile.com. +# +USDT_HEADERS= $(USDT_PROVIDERS:%.d=%_impl.h) +USDT_PICS= $(USDT_PROVIDERS:%.d=pics/%.o) +USDT_G_PICS= $(PICS) + + HSONAME= -h$(SONAME) DYNFLAGS= $(HSONAME) $(ZTEXT) $(ZDEFS) $(BDIRECT) \ $(MAPFILES:%=-M%) $(MAPFILE.PGA:%=-M%) $(MAPFILE.NED:%=-M%) \ @@ -146,7 +165,7 @@ SRCS= $(OBJECTS:%.o=$(SRCDIR)/%.c) # overridden locally when extra processing is needed BUILD.AR= $(AR) $(ARFLAGS) $@ $(AROBJS) BUILD.SO= $(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \ - $(PICS) $(EXTPICS) $(LDLIBS) + $(PICS) $(EXTPICS) $(USDT_PICS) $(LDLIBS) BUILD64.SO= $(CC64) $(CFLAGS64) -o $@ $(GSHARED) $(DYNFLAGS) \ $(PICS) $(EXTPICS) -L $(ROOTLIBDIR64) $(LDLIBS) BUILDCCC.SO= $(CCC) $(CCFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \ diff --git a/usr/src/lib/Makefile.targ b/usr/src/lib/Makefile.targ index a06499a5cb..851f29b395 100644 --- a/usr/src/lib/Makefile.targ +++ b/usr/src/lib/Makefile.targ @@ -86,7 +86,7 @@ $(LIBRARY): objs .WAIT $$(OBJS) $(DYNLIB): $$(MAPFILES) -$(DYNLIB): pics .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS) +$(DYNLIB): pics $(USDT_HEADERS) .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS) .WAIT $(USDT_PICS) $(BUILD.SO) $(POST_PROCESS_SO) diff --git a/usr/src/lib/Makefile.usdt b/usr/src/lib/Makefile.usdt new file mode 100644 index 0000000000..17140161ad --- /dev/null +++ b/usr/src/lib/Makefile.usdt @@ -0,0 +1,28 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# This makefile contains the necessary targets for USDT providers; it should +# be included after Makefile.targ. (It is in a separate file rather than in +# Makefile.targ because the dependency on $(USDT_G_PICS) is incompatible with +# libraries that dynamically define $(OBJECTS).) +# +pics/%.o: $(SRCDIR)/%.d $(USDT_G_PICS) + $(COMPILE.d) -o $@ -s $< $(USDT_G_PICS) + $(POST_PROCESS_O) + +%_impl.h: $(SRCDIR)/%.d + $(DTRACE) -h -o $@ -s $< + diff --git a/usr/src/lib/brand/Makefile b/usr/src/lib/brand/Makefile index 763cdb2fa6..a095ec4a63 100644 --- a/usr/src/lib/brand/Makefile +++ b/usr/src/lib/brand/Makefile @@ -20,17 +20,18 @@ # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2019 Joyent, Inc. # -# lib/brand/Makefile -# -# include global definitions include ../../Makefile.master -# -# Build everything in parallel; use .WAIT for dependencies .PARALLEL: -SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled $($(MACH)_SUBDIRS) +i386_SUBDIRS= bhyve kvm lx +i386_MSGSUBDIRS= lx + +SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled \ + jcommon joyent joyent-minimal $($(MACH)_SUBDIRS) + MSGSUBDIRS= solaris10 shared $($(MACH)_MSGSUBDIRS) all := TARGET= all diff --git a/usr/src/lib/brand/bhyve/Makefile b/usr/src/lib/brand/bhyve/Makefile new file mode 100644 index 0000000000..b95c3cefbd --- /dev/null +++ b/usr/src/lib/brand/bhyve/Makefile @@ -0,0 +1,32 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright (c) 2018, Joyent, Inc. +# + +default: all + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS = zone + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint + +all install clean clobber lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/bhyve/Makefile.bhyve b/usr/src/lib/brand/bhyve/Makefile.bhyve new file mode 100644 index 0000000000..c2ac3fac2c --- /dev/null +++ b/usr/src/lib/brand/bhyve/Makefile.bhyve @@ -0,0 +1,17 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright (c) 2018, Joyent, Inc. +# + +BRAND= bhyve + +include $(SRC)/lib/brand/Makefile.brand + diff --git a/usr/src/lib/brand/bhyve/zone/Makefile b/usr/src/lib/brand/bhyve/zone/Makefile new file mode 100644 index 0000000000..196e31d575 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/Makefile @@ -0,0 +1,45 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2020 Joyent, Inc. +# + +include $(SRC)/cmd/Makefile.cmd +include ../Makefile.bhyve +include $(SRC)/cmd/Makefile.cmd.64 + +PROGS = attach detach statechange uninstall + +PROG1 = boot +PROG2 = bhhwcompat +PROGS += $(PROG1) $(PROG2) +CLEANFILES += $(PROG1) $(PROG2) + +$(PROG1) := LDLIBS += -lnvpair -lcustr +$(PROG2) := CPPFLAGS = -I$(COMPAT)/freebsd -I$(CONTRIB)/freebsd \ + $(CPPFLAGS.master) -I$(SRC)/uts/i86pc \ + -I$(COMPAT)/freebsd/amd64 -I$(CONTRIB)/freebsd/amd64 + +TEMPLATES = SYSbhyve.xml +XMLDOCS = config.xml platform.xml +CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTTEMPLATES) $(ROOTPROGS) + +# This shouldn't be necessary, but for some reason the same thing in +# ../../Makefile.brand is not being picked up. +$(ROOTPROGS) := FILEMODE = 755 + +all: $(PROGS) + +install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES) + +clean: + $(RM) $(CLEANFILES) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/bhyve/zone/SYSbhyve.xml b/usr/src/lib/brand/bhyve/zone/SYSbhyve.xml new file mode 100644 index 0000000000..4fd8cb5406 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/SYSbhyve.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + DO NOT EDIT THIS FILE. Use zonecfg(1M) instead. +--> +<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1"> +<zone name="SYSbhyve" zonepath="" autoboot="false" brand="bhyve" > + <attr name="bootrom" type="string" value="/usr/share/bhyve/uefi-csm-rom.bin"/> + <attr name="com1" type="string" value="/dev/zconsole"/> +</zone> diff --git a/usr/src/lib/brand/bhyve/zone/attach b/usr/src/lib/brand/bhyve/zone/attach new file mode 100755 index 0000000000..626e608579 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/attach @@ -0,0 +1,26 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018, Joyent, Inc. +# + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_kvm_final_setup +} + +ps_brand="bhyve" +. /usr/lib/brand/jcommon/cattach diff --git a/usr/src/lib/brand/bhyve/zone/bhhwcompat.c b/usr/src/lib/brand/bhyve/zone/bhhwcompat.c new file mode 100644 index 0000000000..5f7a20feba --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/bhhwcompat.c @@ -0,0 +1,83 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018, Joyent, Inc. + */ + +/* + * With no option, exit 0 if the current hardware is bhyve-compatible, non-zero + * otherwise. A '-v' option can be used to print the incompatibility reason + * provided by the kernel. + * + * The -c option can be used to print the number of virtual CPUs supported by + * bhyve build. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/vmm.h> + +#include <sys/param.h> +#include <sys/cpuset.h> +#include <sys/vmm.h> +#include <sys/vmm_dev.h> + +static void +usage() +{ + fprintf(stderr, "bhhwcompat [-cv]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int fd, c; + char emsg[128]; + boolean_t max_cpu = B_FALSE; + boolean_t verbose = B_FALSE; + + while ((c = getopt(argc, argv, "cv")) != -1) { + switch (c) { + case 'c': + max_cpu = B_TRUE; + break; + case 'v': + verbose = B_TRUE; + break; + default: + usage(); + } + } + + if (max_cpu) { + (void) printf("%d\n", VM_MAXCPU); + } + + if ((fd = open(VMM_CTL_DEV, O_RDONLY | O_EXCL)) < 0) { + if (verbose) + fprintf(stderr, "missing %s\n", VMM_CTL_DEV); + exit(1); + } + + emsg[0] = '\0'; + if (ioctl(fd, VMM_VM_SUPPORTED, emsg) < 0) { + if (verbose) + fprintf(stderr, "%s\n", emsg); + exit(1); + } + + (void) close(fd); + return (0); +} diff --git a/usr/src/lib/brand/bhyve/zone/boot.c b/usr/src/lib/brand/bhyve/zone/boot.c new file mode 100644 index 0000000000..3d26e350d4 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/boot.c @@ -0,0 +1,926 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Joyent, Inc. + */ + +/* + * This program runs as a child of zoneadmd, which sets a variety of + * _ZONECFG_<resource>_<instance> properties so that child processes don't have + * to parse xml. + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <libnvpair.h> +#include <libcustr.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/debug.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <zone.h> + +/* These two paths must be relative to the zone root. */ +#define BHYVE_DIR "var/run/bhyve" +#define BHYVE_ARGS_FILE BHYVE_DIR "/" "zhyve.cmd" + +#define ZH_MAXARGS 100 + +#define DEFAULT_BOOTROM "/usr/share/bhyve/uefi-rom.bin" +#define DEFAULT_BOOTROM_CSM "/usr/share/bhyve/uefi-csm-rom.bin" + +typedef enum { + PCI_SLOT_HOSTBRIDGE = 0, + PCI_SLOT_CD = 3, /* Windows ahci allows slots 3 - 6 */ + PCI_SLOT_BOOT_DISK, + PCI_SLOT_OTHER_DISKS, + PCI_SLOT_NICS, + PCI_SLOT_FBUF = 30, + PCI_SLOT_LPC = 31, /* Windows requires lpc in slot 31 */ +} pci_slot_t; + +static boolean_t debug; +static const char *zonename; +static const char *zonepath; + +#define dprintf(x) if (debug) (void)printf x + +static char * +get_zcfg_var(const char *rsrc, const char *inst, const char *prop) +{ + char envvar[MAXNAMELEN]; + char *ret; + + if (prop == NULL) { + if (snprintf(envvar, sizeof (envvar), "_ZONECFG_%s_%s", + rsrc, inst) >= sizeof (envvar)) { + return (NULL); + } + } else { + if (snprintf(envvar, sizeof (envvar), "_ZONECFG_%s_%s_%s", + rsrc, inst, prop) >= sizeof (envvar)) { + return (NULL); + } + } + + ret = getenv(envvar); + + dprintf(("%s: '%s=%s'\n", __func__, envvar, ret ? ret : "<null>")); + + return (ret); +} + +static boolean_t +is_env_true(const char *rsrc, const char *inst, const char *prop) +{ + char *val = get_zcfg_var(rsrc, inst, prop); + + return (val != NULL && strcmp(val, "true") == 0); +} + +static boolean_t +is_env_string(const char *rsrc, const char *inst, const char *prop, + const char *val) +{ + char *pval = get_zcfg_var(rsrc, inst, prop); + + return (pval != NULL && strcmp(pval, val) == 0); +} + +static int +add_arg(int *argc, char **argv, const char *val) +{ + if (*argc >= ZH_MAXARGS) { + (void) printf("Error: too many arguments\n"); + return (1); + } + argv[*argc] = strdup(val); + assert(argv[*argc] != NULL); + dprintf(("%s: argv[%d]='%s'\n", __func__, *argc, argv[*argc])); + (*argc)++; + return (0); +} + +static int +add_smbios(int *argc, char **argv) +{ + char smbios[MAXPATHLEN]; + struct utsname utsname; + const char *version; + const char *uuid; + + if ((uuid = getenv("_ZONECFG_uuid")) != NULL) { + if (add_arg(argc, argv, "-U") != 0 || + add_arg(argc, argv, uuid) != 0) + return (1); + } + + /* + * Look for something like joyent_20180329T120303Z. A little mucky, but + * it's exactly what sysinfo does. + */ + (void) uname(&utsname); + if (strncmp(utsname.version, "joyent_", strlen("joyent_")) == 0) + version = utsname.version + strlen("joyent_"); + else + version = "?"; + + /* + * This is based upon the SMBIOS values we expose to KVM guests. + */ + (void) snprintf(smbios, sizeof (smbios), + "1,manufacturer=Joyent,product=SmartDC HVM,version=7.%s," + "serial=%s,sku=001,family=Virtual Machine", + version, zonename); + + if (add_arg(argc, argv, "-B") != 0 || + add_arg(argc, argv, smbios) != 0) + return (1); + + return (0); +} + +static int +add_cpu(int *argc, char **argv) +{ + char *val; + + if ((val = get_zcfg_var("attr", "vcpus", NULL)) != NULL) { + if (add_arg(argc, argv, "-c") != 0 || + add_arg(argc, argv, val) != 0) { + return (1); + } + } + return (0); +} + +static int +add_ram(int *argc, char **argv) +{ + char *val; + + if ((val = get_zcfg_var("attr", "ram", NULL)) != NULL) { + if (add_arg(argc, argv, "-m") != 0 || + add_arg(argc, argv, val) != 0) { + return (1); + } + } + return (0); +} + +static int +parse_pcislot(const char *pcislot, uint_t *busp, uint_t *devp, uint_t *funcp) +{ + char junk; + + switch (sscanf(pcislot, "%u:%u:%u%c", busp, devp, funcp, &junk)) { + case 3: + break; + case 2: + case 1: + *funcp = *devp; + *devp = *busp; + *busp = 0; + break; + default: + (void) printf("Error: device %d has illegal PCI slot: %s\n", + *devp, pcislot); + return (-1); + } + + if (*busp > 255 || *devp > 31 || *funcp > 7) { + (void) printf("Error: device %d has illegal PCI slot: %s\n", + *devp, pcislot); + return (-1); + } + + return (0); +} + +/* + * In the initial implementation, slot assignment was dynamic on every boot. + * Now, each device resource can have a pci_slot property that will override + * dynamic assignment. The original behavior is preserved, but no effort is + * made to detect or avoid conflicts between legacy behavior and new behavior. + * When used with vmadm, this is not an issue, as it will update the zone + * config at boot time to contain static assignments. + */ +static int +add_disk(char *disk, char *path, char *slotconf, size_t slotconf_len) +{ + static char *boot = NULL; + static int next_cd = 0; + static int next_other = 0; + custr_t *sconfstr = NULL; + const char *model = "virtio-blk"; + uint_t pcibus = 0, pcidev = 0, pcifn = 0; + const char *slotstr; + const char *guest_block_size = NULL; + boolean_t isboot; + boolean_t nodelete = B_FALSE; + + if (custr_alloc_buf(&sconfstr, slotconf, slotconf_len) == -1) { + return (-1); + } + + isboot = is_env_true("device", disk, "boot"); + if (isboot) { + /* Allow at most one "primary" disk */ + if (boot != NULL) { + (void) printf("Error: multiple boot disks: %s %s\n", + boot, path); + goto fail; + } + boot = path; + } + + if ((slotstr = get_zcfg_var("device", disk, "pci_slot")) != NULL) { + if (parse_pcislot(slotstr, &pcibus, &pcidev, &pcifn) != 0) { + goto fail; + } + } else { + if (isboot) { + pcidev = PCI_SLOT_BOOT_DISK; + pcifn = 0; + } else if (is_env_string("device", disk, "media", "cdrom")) { + pcidev = PCI_SLOT_CD; + pcifn = next_cd; + next_cd++; + } else { + pcidev = PCI_SLOT_OTHER_DISKS; + pcifn = next_other; + next_other++; + } + } + + if (is_env_string("device", disk, "model", "virtio")) { + model = "virtio-blk"; + /* + * bhyve's blockif code refers to the UNMAP/DISCARD/TRIM + * feature as 'delete' and so 'nodelete' is used by + * bhyve to disable the feature. We use 'trim' for + * interfaces we expose to the operator as that seems to + * be the most familiar name for the operation (and less + * likely to cause confusion). + */ + nodelete = is_env_true("device", disk, "notrim"); + guest_block_size = get_zcfg_var("device", disk, + "guest_block_size"); + + /* Treat a 0 size to mean the whatever the volume advertises */ + if (guest_block_size != NULL && + strcmp(guest_block_size, "0") == 0) { + guest_block_size = NULL; + } + } else if (is_env_string("device", disk, "model", "nvme")) { + model = "nvme"; + } else if (is_env_string("device", disk, "model", "ahci")) { + if (is_env_string("device", disk, "media", "cdrom")) { + model = "ahci-cd"; + } else { + model = "ahci-hd"; + } + } else { + (void) printf("Error: unknown disk model '%s'\n", model); + goto fail; + } + + if (custr_append_printf(sconfstr, "%u:%u:%u,%s,%s", + pcibus, pcidev, pcifn, model, path) == -1) { + (void) printf("Error: disk path '%s' too long\n", path); + goto fail; + } + + if (nodelete && custr_append(sconfstr, ",nodelete") == -1) { + (void) printf("Error: too many disk options\n"); + goto fail; + } + + if (guest_block_size != NULL && custr_append_printf(sconfstr, + ",sectorsize=%s", guest_block_size) == -1) { + (void) printf("Error: too many disk options\n"); + goto fail; + } + + custr_free(sconfstr); + return (0); + +fail: + custr_free(sconfstr); + return (-1); +} + +static int +add_ppt(int *argc, char **argv, char *ppt, char *path, char *slotconf, + size_t slotconf_len) +{ + static boolean_t wired = B_FALSE; + static boolean_t acpi = B_FALSE; + uint_t bus = 0, dev = 0, func = 0; + char *pcislot; + + pcislot = get_zcfg_var("device", ppt, "pci_slot"); + + if (pcislot == NULL) { + (void) printf("Error: device %s has no PCI slot\n", ppt); + return (-1); + } + + if (parse_pcislot(pcislot, &bus, &dev, &func) != 0) { + return (-1); + } + + if (bus > 0) { + if (!acpi && add_arg(argc, argv, "-A") != 0) + return (-1); + acpi = B_TRUE; + } + + if (!wired && add_arg(argc, argv, "-S") != 0) + return (-1); + + wired = B_TRUE; + + if (snprintf(slotconf, slotconf_len, "%d:%d:%d,passthru,%s", + bus, dev, func, path) >= slotconf_len) { + (void) printf("Error: device path '%s' too long\n", path); + return (-1); + } + + return (0); +} + +static int +add_devices(int *argc, char **argv) +{ + char *devices; + char *dev; + char *lasts; + char slotconf[MAXNAMELEN]; + + if ((devices = get_zcfg_var("device", "resources", NULL)) == NULL) { + return (0); + } + + for (dev = strtok_r(devices, " ", &lasts); dev != NULL; + dev = strtok_r(NULL, " ", &lasts)) { + int ret; + char *path; + char *model; + + /* zoneadmd is not careful about a trailing delimiter. */ + if (dev[0] == '\0') { + continue; + } + + if ((path = get_zcfg_var("device", dev, "path")) == NULL) { + (void) printf("Error: device %s has no path\n", dev); + return (-1); + } + + if ((model = get_zcfg_var("device", dev, "model")) == NULL) { + (void) printf("Error: device %s has no model\n", dev); + return (-1); + } + + if (strcmp(model, "passthru") == 0) { + ret = add_ppt(argc, argv, dev, path, slotconf, + sizeof (slotconf)); + } else { + ret = add_disk(dev, path, slotconf, sizeof (slotconf)); + } + + if (ret != 0) + return (-1); + + if (add_arg(argc, argv, "-s") != 0 || + add_arg(argc, argv, slotconf) != 0) { + return (-1); + } + } + + return (0); +} + +static int +add_nets(int *argc, char **argv) +{ + char *nets; + char *net; + char *lasts; + int nextpcifn = 1; /* 0 reserved for primary */ + char slotconf[MAXNAMELEN]; + char *primary = NULL; + + if ((nets = get_zcfg_var("net", "resources", NULL)) == NULL || + strcmp(nets, "") == 0) { + return (0); + } + + for (net = strtok_r(nets, " ", &lasts); net != NULL; + net = strtok_r(NULL, " ", &lasts)) { + int pcifn; + + /* zoneadmd is not careful about a trailing delimiter. */ + if (net[0] == '\0') { + continue; + } + + /* Allow at most one "primary" net */ + if (is_env_true("net", net, "primary")) { + if (primary != NULL) { + (void) printf("Error: " + "multiple primary nets: %s %s\n", + primary, net); + return (-1); + } + primary = net; + pcifn = 0; + } else { + pcifn = nextpcifn; + nextpcifn++; + } + + if (snprintf(slotconf, sizeof (slotconf), + "%d:%d,virtio-net-viona,%s", PCI_SLOT_NICS, pcifn, net) >= + sizeof (slotconf)) { + (void) printf("Error: net '%s' too long\n", net); + return (-1); + } + + if (add_arg(argc, argv, "-s") != 0 || + add_arg(argc, argv, slotconf) != 0) { + return (-1); + } + } + + /* Make sure there is a "primary" net */ + if (primary == NULL) { + (void) printf("Error: no primary net has been specified\n"); + return (-1); + } + + return (0); +} + +static int +add_lpc(int *argc, char **argv) +{ + char *lpcdevs[] = { "bootrom", "com1", "com2", NULL }; + const int bootrom_idx = 0; + int i; + char *val; + char conf[MAXPATHLEN]; + boolean_t found_bootrom = B_FALSE; + + assert(strcmp(lpcdevs[bootrom_idx], "bootrom") == 0); + + (void) snprintf(conf, sizeof (conf), "%d,lpc", PCI_SLOT_LPC); + if (add_arg(argc, argv, "-s") != 0 || + add_arg(argc, argv, conf) != 0) { + return (-1); + } + + for (i = 0; lpcdevs[i] != NULL; i++) { + if ((val = get_zcfg_var("attr", lpcdevs[i], NULL)) == NULL) { + continue; + } + if (i == bootrom_idx) { + found_bootrom = B_TRUE; + if (strcmp(val, "bios") == 0) { + val = DEFAULT_BOOTROM_CSM; + } else if (strcmp(val, "uefi") == 0) { + val = DEFAULT_BOOTROM; + } + } + if (snprintf(conf, sizeof (conf), "%s,%s", lpcdevs[i], val) >= + sizeof (conf)) { + (void) printf("Error: value of attr '%s' too long\n", + lpcdevs[i]); + return (-1); + } + if (add_arg(argc, argv, "-l") != 0 || + add_arg(argc, argv, conf) != 0) { + return (-1); + } + } + + if (!found_bootrom) { + if (add_arg(argc, argv, "-l") != 0 || + add_arg(argc, argv, "bootrom," DEFAULT_BOOTROM_CSM) != 0) { + return (-1); + } + } + + return (0); +} + +static int +add_hostbridge(int *argc, char **argv) +{ + char conf[MAXPATHLEN]; + char *model = NULL; + boolean_t raw_config = B_FALSE; + + if ((model = get_zcfg_var("attr", "hostbridge", NULL)) != NULL) { + /* Easy bypass for doing testing */ + if (strcmp("none", model) == 0) { + return (0); + } + + if (strchr(model, '=') != NULL) { + /* + * If the attribute contains '=', assume the creator + * wants total control over the config. Do not prepend + * the value with 'model='. + */ + raw_config = B_TRUE; + } + } + + /* Default to Natoma if nothing else is specified */ + if (model == NULL) { + model = "i440fx"; + } + + (void) snprintf(conf, sizeof (conf), "%d,hostbridge,%s%s", + PCI_SLOT_HOSTBRIDGE, raw_config ? "" : "model=", model); + if (add_arg(argc, argv, "-s") != 0 || + add_arg(argc, argv, conf) != 0) { + return (-1); + } + return (0); +} + +static int +add_bhyve_extra_opts(int *argc, char **argv) +{ + char *val; + char *tok; + char *lasts; + + if ((val = get_zcfg_var("attr", "bhyve_extra_opts", NULL)) == NULL) { + return (0); + } + + val = strdup(val); + if (val == NULL) { + (void) printf("Error: strdup failed\n"); + return (-1); + } + + for (tok = strtok_r(val, " \t", &lasts); tok != NULL; + tok = strtok_r(NULL, " \t", &lasts)) { + if (tok[0] == '\0') { + continue; + } + if (add_arg(argc, argv, tok) != 0) { + return (-1); + } + } + + free(val); + return (0); +} + +#define INVALID_CHAR (char)(255) + +static char +decode_char(char encoded) +{ + if (encoded >= 'A' && encoded <= 'Z') + return (encoded - 'A'); + if (encoded >= 'a' && encoded <= 'z') + return (encoded - 'a' + 26); + if (encoded >= '0' && encoded <= '9') + return (encoded - '0' + 52); + if (encoded == '+') + return (62); + if (encoded == '/') + return (63); + if (encoded == '=') + return (0); + return (INVALID_CHAR); +} + +static int +add_base64(custr_t *cus, const char *b64) +{ + size_t b64len = strlen(b64); + + if (b64len == 0 || b64len % 4 != 0) + return (-1); + + while (b64len > 0) { + uint_t padding = 0; + char c0 = decode_char(b64[0]); + char c1 = decode_char(b64[1]); + char c2 = decode_char(b64[2]); + char c3 = decode_char(b64[3]); + + if (c0 == INVALID_CHAR || c1 == INVALID_CHAR || + c2 == INVALID_CHAR || c3 == INVALID_CHAR) { + (void) printf("Error: base64 value contains invalid " + "character(s)\n"); + return (-1); + } + + /* + * For each block of 4 input characters, an '=' should + * only appear as the last two characters. + */ + if (b64[0] == '=' || b64[1] == '=') { + (void) printf("Error: base64 value contains invalid " + "padding\n"); + return (-1); + } + + if (b64len == 4) { + /* + * We can end with '==' or '=', but never '=' + * followed by something else. + */ + if (b64[2] == '=') { + if (b64[3] != '=') { + (void) printf("Error: base64 value " + "contains invalid padding\n"); + return (-1); + } + padding = 2; + } else if (b64[3] == '=') { + padding = 1; + } + } + + VERIFY0(custr_appendc(cus, c0 << 2 | c1 >> 4)); + if (padding < 2) + VERIFY0(custr_appendc(cus, c1 << 4 | c2 >> 2)); + if (padding < 1) + VERIFY0(custr_appendc(cus, c2 << 6 | c3)); + + b64len -= 4; + b64 += 4; + } + + return (0); +} + +/* + * Adds the frame buffer and an xhci tablet to help with the pointer. + */ +static int +add_fbuf(int *argc, char **argv) +{ + char conf[MAXPATHLEN]; + custr_t *cconf = NULL; + char *password = NULL; + + /* + * Do not add a frame buffer or tablet if VNC is disabled. + */ + if (is_env_string("attr", "vnc_port", NULL, "-1")) { + return (0); + } + + if (custr_alloc_buf(&cconf, conf, sizeof (conf)) != 0) { + return (-1); + } + + VERIFY0(custr_append_printf(cconf, "%d:0,fbuf,vga=off,unix=/tmp/vm.vnc", + PCI_SLOT_FBUF)); + + password = get_zcfg_var("attr", "vnc_password", NULL); + if (password != NULL) { + VERIFY0(custr_append(cconf, ",password=")); + + if (add_base64(cconf, password) != 0) { + goto fail; + } + } + + if (add_arg(argc, argv, "-s") != 0 || + add_arg(argc, argv, conf) != 0) { + goto fail; + } + + custr_reset(cconf); + VERIFY0(custr_append_printf(cconf, "%d:1,xhci,tablet", PCI_SLOT_FBUF)); + + if (add_arg(argc, argv, "-s") != 0 || + add_arg(argc, argv, conf) != 0) { + goto fail; + } + + /* + * Since cconf was allocated using custr_alloc_buf() where 'conf' + * is the underlying fixed buffer for cconf, we can free cconf + * which in this instance will just free cconf, but _not_ the + * underlying fixed buffer (conf) which is left unchanged by + * custr_free(). + */ + + custr_free(cconf); + return (0); + +fail: + custr_free(cconf); + return (-1); +} + +/* Must be called last */ +static int +add_vmname(int *argc, char **argv) +{ + char buf[229]; /* VM_MAX_NAMELEN */ + char *val = getenv("_ZONECFG_did"); + + if (val == NULL || val[0] == '\0') { + val = "SYSbhyve-unknown"; + } else { + (void) snprintf(buf, sizeof (buf), "SYSbhyve-%s", val); + val = buf; + } + if (add_arg(argc, argv, val) != 0) { + return (-1); + } + return (0); +} + +/* + * Write the entire buffer or return an error. This function could be more + * paranoid and call fdsync() at the end. That's not really need for this use + * case because it is being written to tmpfs. + */ +static int +full_write(int fd, char *buf, size_t buflen) +{ + ssize_t nwritten; + size_t totwritten = 0; + + while (totwritten < buflen) { + nwritten = write(fd, buf + totwritten, buflen - totwritten); + if (nwritten < 0) { + if (errno == EAGAIN || errno == EINTR) { + continue; + } + return (-1); + } + assert(nwritten > 0); + totwritten += nwritten; + } + assert(totwritten == buflen); + + return (0); +} + +static void +init_debug(void) +{ + char *val = getenv("_ZONEADMD_brand_debug"); + + debug = (val != NULL && val[0] != '\0'); +} + +static int +setup_reboot(void) +{ + zoneid_t zoneid; + + if ((zoneid = getzoneidbyname(zonename)) < 0) { + (void) printf("Error: bhyve zoneid (%s) does not exist\n", + zonename); + return (-1); + } + + if (zoneid == GLOBAL_ZONEID) { + (void) printf("Error: bhyve global zoneid (%s)\n", zonename); + return (-1); + } + + if (zone_setattr(zoneid, ZONE_ATTR_INITRESTART0, NULL, 0) < 0) { + (void) printf("Error: bhyve zoneid %ld setattr failed: %s\n", + zoneid, strerror(errno)); + return (-1); + } + + return (0); +} + +int +main(int argc, char **argv) +{ + int fd, err; + char *zhargv[ZH_MAXARGS] = { + "bhyve", /* Squats on argv[0] */ + "-H", /* vmexit on halt isns */ + NULL }; + int zhargc = 2; + nvlist_t *nvl; + char *nvbuf = NULL; + size_t nvbuflen = 0; + char zoneroot[MAXPATHLEN]; + int zrfd; + + init_debug(); + + if (argc != 3) { + (void) printf("Error: bhyve boot program called with " + "%d args, expecting 2\n", argc - 1); + return (1); + } + zonename = argv[1]; + zonepath = argv[2]; + + if (setup_reboot() < 0) + return (1); + + if (add_smbios(&zhargc, (char **)&zhargv) != 0 || + add_lpc(&zhargc, (char **)&zhargv) != 0 || + add_hostbridge(&zhargc, (char **)&zhargv) != 0 || + add_cpu(&zhargc, (char **)&zhargv) != 0 || + add_ram(&zhargc, (char **)&zhargv) != 0 || + add_devices(&zhargc, (char **)&zhargv) != 0 || + add_nets(&zhargc, (char **)&zhargv) != 0 || + add_bhyve_extra_opts(&zhargc, (char **)&zhargv) != 0 || + add_fbuf(&zhargc, (char **)&zhargv) != 0 || + add_vmname(&zhargc, (char **)&zhargv) != 0) { + return (1); + } + + /* + * This and other dynamically allocated resources are intentionally + * leaked. It's a short-lived program and it will all get mopped up on + * exit. + */ + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_string_array(nvl, "bhyve_args", zhargv, zhargc) != 0) { + (void) printf("Error: failed to create nvlist: %s\n", + strerror(errno)); + return (1); + } + + if (debug) { + dprintf(("packing nvlist:\n")); + nvlist_print(stdout, nvl); + } + + err = nvlist_pack(nvl, &nvbuf, &nvbuflen, NV_ENCODE_XDR, 0); + if (err != 0) { + (void) printf("Error: failed to pack nvlist: %s\n", + strerror(err)); + return (1); + } + + if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >= + sizeof (zoneroot)) { + (void) printf("Error: zonepath '%s' too long\n", zonepath); + return (1); + } + + if ((zrfd = open(zoneroot, O_RDONLY|O_SEARCH)) < 0) { + (void) printf("Error: cannot open zone root '%s': %s\n", + zoneroot, strerror(errno)); + return (1); + } + + /* + * This mkdirat() and the subsequent openat() are only safe because the + * zone root is always under the global zone's exclusive control (always + * read-only in all zones) and the writable directory is a tmpfs file + * system that was just mounted and no zone code has run yet. + */ + if (mkdirat(zrfd, BHYVE_DIR, 0700) != 0 && errno != EEXIST) { + (void) printf("Error: failed to create directory %s " + "in zone: %s\n", BHYVE_DIR, strerror(errno)); + return (1); + } + + fd = openat(zrfd, BHYVE_ARGS_FILE, O_WRONLY|O_CREAT|O_EXCL, 0600); + if (fd < 0) { + (void) printf("Error: failed to create file %s in zone: %s\n", + BHYVE_ARGS_FILE, strerror(errno)); + return (1); + } + if (full_write(fd, nvbuf, nvbuflen) != 0) { + (void) printf("Error: failed to write %s: %s\n", + BHYVE_ARGS_FILE, strerror(errno)); + (void) unlink(BHYVE_ARGS_FILE); + return (1); + } + + return (0); +} diff --git a/usr/src/lib/brand/bhyve/zone/config.xml b/usr/src/lib/brand/bhyve/zone/config.xml new file mode 100644 index 0000000000..82b95f4e57 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/config.xml @@ -0,0 +1,69 @@ +<?xml version="1.0"?> + +<!-- + This file and its contents are supplied under the terms of the + Common Development and Distribution License ("CDDL"), version 1.0. + You may only use this file in accordance with the terms of version + 1.0 of the CDDL. + + A full copy of the text of the CDDL should have accompanied this + source. A copy of the CDDL is also available via the Internet at + http://www.illumos.org/license/CDDL. + + Copyright (c) 2018, Joyent, Inc. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN" + "file:///usr/share/lib/xml/dtd/brand.dtd.1"> + +<brand name="bhyve"> + <modname></modname> + + <initname>/usr/lib/brand/bhyve/zhyve</initname> + <login_cmd /> + <forcedlogin_cmd /> + <user_cmd /> + + <!-- XXX-mg + Until we have better separation of concerns, bhyve brand will use the + kvm installer, which is intertwined with vmadm. + --> + <install>/usr/lib/brand/kvm/kinstall -z %z -R %R</install> + <installopts>R:t:U:q:z:</installopts> + <boot>/usr/lib/brand/bhyve/boot %z %R</boot> + <halt /> + <verify_cfg /> + <verify_adm /> + <postclone /> + <postinstall /> + <attach>/usr/lib/brand/bhyve/attach -z %z -R %R</attach> + <detach>/usr/lib/brand/bhyve/detach -z %z -R %R</detach> + <clone /> + <uninstall>/usr/lib/brand/bhyve/uninstall -z %z -R %R</uninstall> + <prestatechange>/usr/lib/brand/bhyve/statechange pre %z %R</prestatechange> + <poststatechange>/usr/lib/brand/bhyve/statechange post %z %R</poststatechange> + + <privilege set="default" name="net_rawaccess" ip-type="exclusive" /> + <privilege set="default" name="proc_clock_highres" /> + <privilege set="default" name="proc_lock_memory" /> + <privilege set="default" name="sys_admin" /> + <privilege set="default" name="sys_mount" /> + + <privilege set="prohibited" name="dtrace_kernel" /> + <privilege set="prohibited" name="proc_zone" /> + <privilege set="prohibited" name="sys_config" /> + <privilege set="prohibited" name="sys_devices" /> + <privilege set="prohibited" name="sys_ip_config" ip-type="shared" /> + <privilege set="prohibited" name="sys_linkdir" /> + <privilege set="prohibited" name="sys_net_config" /> + <privilege set="prohibited" name="sys_res_config" /> + <privilege set="prohibited" name="sys_suser_compat" /> + <privilege set="prohibited" name="xvm_control" /> + <privilege set="prohibited" name="virt_manage" /> + <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" /> + + <privilege set="required" name="proc_exec" /> + <privilege set="required" name="sys_mount" /> +</brand> diff --git a/usr/src/lib/brand/bhyve/zone/detach b/usr/src/lib/brand/bhyve/zone/detach new file mode 100755 index 0000000000..8a41815258 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/detach @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018, Joyent, Inc. +# + +ps_brand="bhyve" +. /usr/lib/brand/jcommon/cdetach diff --git a/usr/src/lib/brand/bhyve/zone/platform.xml b/usr/src/lib/brand/bhyve/zone/platform.xml new file mode 100644 index 0000000000..4f4446b345 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/platform.xml @@ -0,0 +1,68 @@ +<?xml version="1.0"?> + +<!-- + This file and its contents are supplied under the terms of the + Common Development and Distribution License ("CDDL"), version 1.0. + You may only use this file in accordance with the terms of version + 1.0 of the CDDL. + + A full copy of the text of the CDDL should have accompanied this + source. A copy of the CDDL is also available via the Internet at + http://www.illumos.org/license/CDDL. + + Copyright (c) 2018, Joyent, Inc. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN" + "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1"> + +<platform name="bhyve" allow-exclusive-ip="true"> + + <!-- Global filesystems to mount when booting the zone --> + <global_mount special="/dev" directory="/dev" type="dev" + opt="attrdir=%R/root/dev"/> + <global_mount special="/lib" directory="/lib" + opt="ro,nodevices,nosub" type="lofs" /> + <global_mount special="/usr" directory="/usr" + opt="ro,nodevices,nosub" type="lofs" /> + + <!-- Local filesystems to mount when booting the zone --> + <mount special="/proc" directory="/proc" type="proc" /> + <mount special="swap" directory="/var/run" type="tmpfs" + opt="size=50m"/> + + <!-- Needed by dlmgmtd --> + <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" + opt="size=10m" /> + + <!-- Devices to create under /dev --> + <device match="dld" ip-type="exclusive" /> + <device match="fd" /> + <device match="null" /> + <device match="random" /> + <device match="rdsk" /> + <device match="viona" /> + + <!-- + Careful: this is not just about catching both /dev/vmmctl and the + /dev/vmm directory. In fact it's required to prevent a race where + sdev processes "/dev/vmm/", sees it doesn't exist in the global + /dev (as the module isn't loaded), then never updates its own sdev + directory. Adding the glob here ensures it's checked as needed. + --> + <device match="vmm*" /> + + <!-- Renamed devices to create under /dev --> + <device match="zcons/%z/zoneconsole" name="zconsole" /> + <device match="zfd/%z/slave/1" name="zfd/1" /> + <device match="zfd/%z/slave/2" name="zfd/2" /> + + <!-- Symlinks to create under /dev --> + <symlink source="console" target="zconsole" /> + <symlink source="stderr" target="./fd/2" /> + <symlink source="stdin" target="./fd/0" /> + <symlink source="stdout" target="./fd/1" /> + +</platform> diff --git a/usr/src/lib/brand/bhyve/zone/statechange b/usr/src/lib/brand/bhyve/zone/statechange new file mode 100644 index 0000000000..76cfbffc17 --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/statechange @@ -0,0 +1,22 @@ +#! /bin/ksh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright (c) 2018, Joyent, Inc. +# + +ps_brand=bhyve + +typeset -A FORCED_ATTRS +FORCED_ATTRS["zlog-mode"]=g-- +FORCED_ATTRS["zlog-name"]=platform.log + +. /usr/lib/brand/jcommon/statechange diff --git a/usr/src/lib/brand/bhyve/zone/uninstall b/usr/src/lib/brand/bhyve/zone/uninstall new file mode 100755 index 0000000000..136044ad9e --- /dev/null +++ b/usr/src/lib/brand/bhyve/zone/uninstall @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +function jcommon_uninstall_hook +{ + : +} + +. /usr/lib/brand/jcommon/cuninstall diff --git a/usr/src/lib/brand/ipkg/zone/Makefile b/usr/src/lib/brand/ipkg/zone/Makefile index bbd1ef7305..b88e80ebe9 100644 --- a/usr/src/lib/brand/ipkg/zone/Makefile +++ b/usr/src/lib/brand/ipkg/zone/Makefile @@ -23,8 +23,10 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2019 Joyent, Inc. +# -TEMPLATES= SUNWdefault.xml SUNWblank.xml +TEMPLATES= SUNWblank.xml XMLDOCS= config.xml platform.xml CLOBBERFILES= $(ROOTXMLDOCS) $(ROOTTEMPLATES) diff --git a/usr/src/lib/brand/jcommon/Makefile b/usr/src/lib/brand/jcommon/Makefile new file mode 100644 index 0000000000..59bc0de644 --- /dev/null +++ b/usr/src/lib/brand/jcommon/Makefile @@ -0,0 +1,28 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2020 Joyent, Inc. +# + +PROGS = cattach cdetach cinstall cuninstall libhooks.ksh \ + poststate prestate query statechange +BRAND = jcommon +FILEMODE = 0444 + +include ../Makefile.brand + +clean: + +clobber: + $(RM) $(ROOTPROGS) + +all: + +install: $(ROOTPROGS) diff --git a/usr/src/lib/brand/jcommon/README.smf b/usr/src/lib/brand/jcommon/README.smf new file mode 100644 index 0000000000..64cd3ade4f --- /dev/null +++ b/usr/src/lib/brand/jcommon/README.smf @@ -0,0 +1,107 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +There are a few rather brand specific issues related to how SMF works inside of +them. This README describes how the different pieces interact and where they are +defined. Note that this README only applies to the Joyent-style sparse zones. It +does not apply to the KVM brand or the traditional ipkg and S10 branded zones. + +# +# Per-brand manifests file +# + +Each brand has a file called `manifests`. This file lists the set of manifests +that the brand cares about being available to the zone. It is formatted as: + +<manifest_name> <enabled | disabled> + +Examples are: + +network/smb/client.xml disabled +network/ssh.xml enabled + +The use of enabled or disabled determines the default disposition of the service +when it is imported. + +This list is used in various places throughout the rest of the system for +determining what shows up in the SMF repositories by default and what shows up +in /lib/svc/manifest. + +# +# Files in /lib/svc/manifest +# + +/lib/svc/manifest is part of the sparse filesystem that gets placed into every +zone. Unlike /usr, /lib/svc/manifest is brand specific. The zones service +(svc:/system/zones:default) is responsible for creating the per-brand +/lib/svc/manifest directories and they live in /zones/manifests/<brand-name>. +This brand specific directory is lofs-mounted read-only into each zone. + +The presence of the enabled and disabled option in the brand's manifests file +determine whether or not the service is enabled by default when imported. The +xml file is changed to match the setting. + +# +# Initial SMF repositories +# + +SMF in the minimal brand works differently than it does in the normal Joyent +Brand when it comes to specifying the initial services that are inside of the +dataset and what files are in the SMF repository. + +SMF has the notion of a `seed repository`. This repository is the initial one +that is used or copied for new zones. This repository contains various services +already imported, whether or not they are enabled or disabled, and the various +service properties. + +The traditional `joyent` brand gets this from the dataset itself. In other words, +the database is already populated with the proper SMF state. + +In the `joyent-minimal` brand we handle this differently. We want to be able to +reuse the datasets that exist but not be stuck with their rather large seed +repositories that contain many things which are harmful in the minimal context, +particularly manifest import (both the early and normal kind). To handle this +the joyent-minimal brand defines a seed repository of its own that gets +installed at zone creation time and replaces any existing repository. + +This seed repository is generated using the `svc.configd-native` and +`svccfg-native` binaries. Every manifest listed in the brand's manifests file is +included. + +Manifests available to the `joyent` brand not imported into the database are +available for manual import in /lib/svc/manifest. With the minimal brand, only +the bare minimum number of manifests should be imported. + +# +# Using non-imported manifests +# + +To use one of the manifests that exists but hasn't been imported is pretty easy. +At some point in time after the initial creation of the zone (during the first +boot setup script for example), you can import the service. For example, if you +were going to import the cron service you would run: + +svccfg import /lib/svc/manifest/system/cron.xml + +Next, you need to potentially enable the service depending on the default +disposition of the service. You enable the service by running: + +svcadm enable -s <service> + +Adding the `-s` flag causes the enabling to be synchronous. If you do not +include the flag then it will poke svc.startd to enable the service and return. +If the service is already enabled by default, then this is safe to run and it +won't change anything. It is safer to just always enable or disable the service +after importing it based on your needs. diff --git a/usr/src/lib/brand/jcommon/cattach b/usr/src/lib/brand/jcommon/cattach new file mode 100644 index 0000000000..4f2e65ad83 --- /dev/null +++ b/usr/src/lib/brand/jcommon/cattach @@ -0,0 +1,63 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +. /usr/lib/brand/shared/common.ksh + +ZONENAME="" +ZONEPATH="" +# Default to 10GB diskset quota +ZQUOTA=10 + +while getopts "R:t:U:q:z:" opt +do + case "$opt" in + R) ZONEPATH="$OPTARG";; + q) ZQUOTA="$OPTARG";; + z) ZONENAME="$OPTARG";; + *) printf "$m_usage\n" + exit $ZONE_SUBPROC_USAGE;; + esac +done +shift OPTIND-1 + +if [[ -z $ZONEPATH || -z $ZONENAME ]]; then + print -u2 "Brand error: No zone path or name" + exit $ZONE_SUBPROC_USAGE +fi + +# The dataset quota must be a number. +case $ZQUOTA in *[!0-9]*) + print -u2 "Brand error: The quota $ZQUOTA is not a number" + exit $ZONE_SUBPROC_USAGE;; +esac + +ZROOT=$ZONEPATH/root + +# Get the dataset of the parent directory of the zonepath. +dname=${ZONEPATH%/*} +bname=${ZONEPATH##*/} +PDS_NAME=`mount | nawk -v p=$dname '{if ($1 == p) print $3}'` +[ -z "$PDS_NAME" ] && \ + print -u2 "Brand error: missing parent ZFS dataset for $dname" + +jcommon_attach_hook + +exit $ZONE_SUBPROC_OK diff --git a/usr/src/lib/brand/jcommon/cdetach b/usr/src/lib/brand/jcommon/cdetach new file mode 100644 index 0000000000..4b67b31fc5 --- /dev/null +++ b/usr/src/lib/brand/jcommon/cdetach @@ -0,0 +1,50 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +. /usr/lib/brand/shared/common.ksh + +ZONENAME="" +ZONEPATH="" + +while getopts "R:t:U:z:" opt +do + case "$opt" in + R) ZONEPATH="$OPTARG";; + z) ZONENAME="$OPTARG";; + *) printf "$m_usage\n" + exit $ZONE_SUBPROC_USAGE;; + esac +done +shift OPTIND-1 + +if [[ -z $ZONEPATH || -z $ZONENAME ]]; then + print -u2 "Brand error: No zone path or name" + exit $ZONE_SUBPROC_USAGE +fi + +# +# We just need a brand hook so that we can bypass the mount checking +# that zoneadm does. This is needed because we have the cores dataset +# mounted under {zonepath}/root. +# +cp /etc/zones/${ZONENAME}.xml ${ZONEPATH}/SUNWdetached.xml + +exit $ZONE_SUBPROC_OK diff --git a/usr/src/lib/brand/jcommon/cinstall b/usr/src/lib/brand/jcommon/cinstall new file mode 100644 index 0000000000..98b9322b5f --- /dev/null +++ b/usr/src/lib/brand/jcommon/cinstall @@ -0,0 +1,183 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2016, Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +. /usr/lib/brand/shared/common.ksh + +REPROVISONING="" +ZONENAME="" +ZONEPATH="" +# Default to 10GB diskset quota +ZQUOTA=10 +ZVOL_NAME=`/usr/bin/uuid -v 4` + +# +# The following are the list of features that a corresponding brand may +# enable. If they wish to do so, then they must set the following flags +# to values such that [[ -z $flag ]] is true. The following are the +# currently supported flags: +# +# o jst_reprovision - Brand supports reprovision +# o jst_tmplopt - Template image optional +# + +function fixup_images +{ + # New imgadm renames the dataset's snapshot at import to @final for us + # and when it exists, we use that. However, when it does not exist we + # still use the old method of creating a zones/<uuid>@<uuid> snapshot + # so we can support old datasets. + exists=$(zfs list -Ho name ${PDS_NAME}/${TMPLZONE}@final 2>&1) + if [[ $? == 0 && ${exists} == "${PDS_NAME}/${TMPLZONE}@final" ]]; then + zfs clone -o devices=off -F ${QUOTA_ARG} ${PDS_NAME}/${TMPLZONE}@final \ + ${PDS_NAME}/${bname} || fatal "failed to clone zone dataset" + elif [[ ${exists} =~ "dataset does not exist" ]]; then + zfs snapshot ${PDS_NAME}/${TMPLZONE}@${bname} + zfs clone -o devices=off -F ${QUOTA_ARG} ${PDS_NAME}/${TMPLZONE}@${bname} \ + ${PDS_NAME}/${bname} || fatal "failed to clone zone dataset" + else + fatal "Unable to determine snapshot for ${PDS_NAME}/${TMPLZONE}" + fi +} + +while getopts "rR:t:U:q:z:" opt +do + case "$opt" in + r) + set -x + if [[ -z "$jst_reprovision" ]]; then + print -u2 "unsupported reprovision requested" + exit $ZONE_SUBPROC_USAGE + fi + REPROVISIONING="true" + ;; + R) ZONEPATH="$OPTARG";; + t) TMPLZONE="$OPTARG";; + # UUID is only used in the postinstall script + U) UUID="$OPTARG";; + q) ZQUOTA="$OPTARG";; + z) ZONENAME="$OPTARG";; + *) printf "$m_usage\n" + exit $ZONE_SUBPROC_USAGE;; + esac +done +shift OPTIND-1 + +# +# IMPORTANT: all actions below need to consider reprovision. If the action +# modifies files in the zoneroot itself, it should be run on reprovision +# any other changes to the zone or dataset should not be done on reprovision. +# + +if [[ -z ${REPROVISIONING} \ + && -n $(zonecfg -z "${ZONENAME}" info attr name=transition \ + | grep "value: receiving:") ]]; then + + # Here we're doing an install for a received zone, the dataset should have + # already been created. + exit $ZONE_SUBPROC_OK +fi + +if [[ -z $ZONEPATH || -z $ZONENAME ]]; then + print -u2 "Brand error: No zone path or name" + exit $ZONE_SUBPROC_USAGE +fi + +if [[ -z ${REPROVISIONING} ]]; then + # The install may requires a template zone. + if [[ -z $TMPLZONE && -z "$jst_tmplopt" ]]; then + print -u2 "Brand error: a zone template is required" + exit $ZONE_SUBPROC_USAGE + fi + + # The dataset quota must be a number. + case $ZQUOTA in *[!0-9]*) + print -u2 "Brand error: The quota $ZQUOTA is not a number" + exit $ZONE_SUBPROC_USAGE;; + esac +fi + +ZROOT=$ZONEPATH/root + +if [[ -z ${REPROVISIONING} ]]; then + # Get the dataset of the parent directory of the zonepath. + dname=${ZONEPATH%/*} + bname=${ZONEPATH##*/} + PDS_NAME=`mount | nawk -v p=$dname '{if ($1 == p) print $3}'` + [ -z "$PDS_NAME" ] && \ + print -u2 "Brand error: missing parent ZFS dataset for $dname" + + # We expect that zoneadm was invoked with '-x nodataset', so it won't have + # created the dataset. + + QUOTA_ARG= + if [[ ${ZQUOTA} != "0" ]]; then + QUOTA_ARG="-o quota=${ZQUOTA}g" + fi + + # + # Some zone brands (KVM) optionally have a top level dataset. If + # they don't have one, we need to set the quota on the data + # disk. Otherwise, we set it on the normal top level dataset. + # + if [[ -z ${TMPLZONE} ]]; then + zfs set quota=${ZQUOTA}g ${PDS_NAME}/${bname} + else + fixup_images + fi + +fi + +# The rest should be run when REPROVISIONING is set as well. + +# Make sure zoneinit is setup to use -o xtrace, this handles old datasets where +# is not yet enabled by default. +if [[ -f ${ZROOT}/root/zoneinit && \ + -z $(grep "^set -o xtrace" ${ZROOT}/root/zoneinit) ]]; then + sed -i "" -e "s/^#set -o xtrace/set -o xtrace/" ${ZROOT}/root/zoneinit +fi + +if [ ! -d ${ZONEPATH}/config ]; then + mkdir -p ${ZONEPATH}/config + chmod 755 ${ZONEPATH}/config +fi + +if [ ! -d ${ZROOT}/tmp ]; then + mkdir -p ${ZROOT}/tmp + chmod 1777 ${ZROOT}/tmp +fi + +# make /var/svc for the 'provisioning file' +if [ ! -d ${ZROOT}/var/svc ]; then + mkdir -p ${ZROOT}/var/svc + chmod 0755 ${ZROOT}/var/svc +fi + +jcommon_attach_hook + +exit $ZONE_SUBPROC_OK diff --git a/usr/src/lib/brand/jcommon/cuninstall b/usr/src/lib/brand/jcommon/cuninstall new file mode 100644 index 0000000000..9be35bc7a9 --- /dev/null +++ b/usr/src/lib/brand/jcommon/cuninstall @@ -0,0 +1,74 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2016 Joyent, Inc. All rights reserved. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +. /usr/lib/brand/shared/common.ksh + +ZONENAME="" +ZONEPATH="" + +while getopts "FR:z:" opt +do + case "$opt" in + F) ;; + R) ZONEPATH="$OPTARG";; + z) ZONENAME="$OPTARG";; + *) printf "$m_usage\n" + exit $ZONE_SUBPROC_USAGE;; + esac +done +shift OPTIND-1 + +if [[ -z $ZONEPATH || -z $ZONENAME ]]; then + print -u2 "Brand error: No zone path or name" + exit $ZONE_SUBPROC_USAGE +fi + +# Get the dataset of the parent directory of the zonepath. +dname=${ZONEPATH%/*} +bname=${ZONEPATH##*/} +PDS_NAME=`mount | nawk -v p=$dname '{if ($1 == p) print $3}'` +if [[ -z "$PDS_NAME" ]]; then + print -u2 "Brand error: missing parent ZFS dataset for $dname" + exit $ZONE_SUBPROC_USAGE +fi + +# check if the origin is not an @final dataset, if not, we'll destroy it +ORIGIN=`zfs get -H -ovalue origin $PDS_NAME/$bname | grep -v "@final$"` + +zfs destroy -rF $PDS_NAME/cores/$bname +zfs destroy -rF $PDS_NAME/$bname +if [[ $? != 0 ]]; then + echo "processes in zone: " + fuser ${ZONEPATH} + # Since we are destroying the zone, we don't want to leave a zoneroot behind + # just because something couldn't be umounted. So we'll also force the + # umount with the 'f' option here. + zfs destroy -rfF $PDS_NAME/$bname +fi + +[[ -n ${ORIGIN} && ${ORIGIN} != "-" ]] && zfs destroy -F $ORIGIN + +rm -rf $ZONEPATH +rm -rf /var/zonecontrol/${ZONENAME} + +jcommon_uninstall_hook + +exit $ZONE_SUBPROC_OK diff --git a/usr/src/lib/brand/jcommon/libhooks.ksh b/usr/src/lib/brand/jcommon/libhooks.ksh new file mode 100644 index 0000000000..913d81899b --- /dev/null +++ b/usr/src/lib/brand/jcommon/libhooks.ksh @@ -0,0 +1,86 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# This file contains various hooks that are used by more than a single +# brand. This file should be included by the brand-specific files. +# + +jattach_kvm_final_setup() +{ + ZRAM=$(zonecfg -z ${ZONENAME} info attr name=ram | \ + grep "value: " | cut -d ':' -f2 | tr -d ' ') + + if [[ -z ${ZRAM} ]]; then + echo "Unable to find RAM value for KVM VM" + exit $ZONE_SUBPROC_FATAL + fi + + # 100G unless the VM has 80G or more DRAM, in which case: DRAM + 20G. + CORE_QUOTA=102400 + if [[ ${ZRAM} -gt 81920 ]]; then + CORE_QUOTA=$((${ZRAM} + 20480)) + fi + + # The cores quota exists to control run-away zones. As such we make it + # such that it will protect the system from a single run-away, but + # still allow us to get most cores. + rm -rf $ZONEPATH/cores + zfs create -o quota=${CORE_QUOTA}m -o mountpoint=/${PDS_NAME}/$bname/cores \ + ${PDS_NAME}/cores/$bname +} + +jattach_zone_final_setup() +{ + if [[ -z ${REPROVISIONING} ]]; then + # The cores quota exists to control run-away zones. As such we make it + # such that it will protect the system from a single run-away, but + # still allow us to get most cores. 100G seems good enough based on + # samples from JPC. + rm -rf $ZONEPATH/cores + CORE_QUOTA=102400 + zfs create -o quota=${CORE_QUOTA}m -o mountpoint=/${PDS_NAME}/$bname/cores \ + ${PDS_NAME}/cores/$bname + + chmod 700 $ZONEPATH + fi + + egrep -s "netcfg:" $ZROOT/etc/passwd + if (( $? != 0 )); then + echo "netcfg:x:17:65:Network Configuration Admin:/:" \ + >> $ZROOT/etc/passwd + echo "netcfg:*LK*:::::::" >> $ZROOT/etc/shadow + fi + egrep -s "netadm:" $ZROOT/etc/group + (( $? != 0 )) && echo "netadm::65:" >> $ZROOT/etc/group + + # /etc/svc/profile needs to be a directory with some contents which we + # can get from the template. The early manifest import svc + # (lib/svc/method/manifest-import) copies some symlinks from the + # template's var/svc/profile dir and we need to make sure those are + # pointing at the right files and not left dangling. + ZPROFILE=$ZROOT/etc/svc/profile + if [ ! -d $ZPROFILE ]; then + mkdir $ZPROFILE + cp -p $ZROOT/var/svc/profile/generic_limited_net.xml $ZPROFILE + cp -p $ZROOT/var/svc/profile/inetd_generic.xml $ZPROFILE + cp -p $ZROOT/var/svc/profile/ns_dns.xml $ZPROFILE + cp -p $ZROOT/var/svc/profile/platform_none.xml $ZPROFILE + fi + + touch $ZROOT/var/log/courier.log +} diff --git a/usr/src/lib/brand/jcommon/poststate b/usr/src/lib/brand/jcommon/poststate new file mode 100644 index 0000000000..4e4b1207ed --- /dev/null +++ b/usr/src/lib/brand/jcommon/poststate @@ -0,0 +1,34 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +if [[ -n $_ZONEADMD_brand_debug ]]; then + logfile=/var/log/zone_bh.$1 + date >>$logfile + echo "zone $1 post-state-change $3 $4" >>$logfile + ksh -x /usr/lib/brand/$ps_brand/statechange "post" $@ 2>>$logfile + res=$? + echo "zone $1 post-state-change result $res" >>$logfile +else + /usr/lib/brand/$ps_brand/statechange "post" $@ + res=$? +fi + +exit $res diff --git a/usr/src/lib/brand/jcommon/prestate b/usr/src/lib/brand/jcommon/prestate new file mode 100644 index 0000000000..a61af89752 --- /dev/null +++ b/usr/src/lib/brand/jcommon/prestate @@ -0,0 +1,34 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +if [[ -n $_ZONEADMD_brand_debug ]]; then + logfile=/var/log/zone_bh.$1 + date >>$logfile + echo "zone $1 pre-state-change $3 $4" >>$logfile + ksh -x /usr/lib/brand/$ps_brand/statechange "pre" $@ 2>>$logfile + res=$? + echo "zone $1 pre-state-change result $res" >>$logfile +else + /usr/lib/brand/$ps_brand/statechange "pre" $@ + res=$? +fi + +exit $res diff --git a/usr/src/lib/brand/jcommon/query b/usr/src/lib/brand/jcommon/query new file mode 100755 index 0000000000..70ea39d8e2 --- /dev/null +++ b/usr/src/lib/brand/jcommon/query @@ -0,0 +1,52 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# + +PATH=/usr/bin:/usr/sbin +export PATH + +. /usr/lib/brand/shared/common.ksh + +zonename=$1 +zonepath=$2 +cmd=$3 + +if [ $3 == "env" ]; then + # + # zoneadmd reads one (arbitrary length) line of input from the query + # hook. If there is more than one environment variable to pass back, + # delimit each one with tabs. zoneadmd will split the line at the tabs + # and set each key/value pair in its environment. + # + # Currently, only _ZONEADMD_ZPOOL is used to set the %P substitution + # for the brand configuration. + # + entry=$(svccfg -s smartdc/init listprop '*/zpool') + if [ -n "$entry" ]; then + val=${entry##* * } + [ -n "$val" ] && echo "_ZONEADMD_ZPOOL=/${val}\c" + fi +fi + +exit $ZONE_SUBPROC_OK diff --git a/usr/src/lib/brand/jcommon/statechange b/usr/src/lib/brand/jcommon/statechange new file mode 100644 index 0000000000..58eae5ab5a --- /dev/null +++ b/usr/src/lib/brand/jcommon/statechange @@ -0,0 +1,906 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018 Joyent, Inc. All rights reserved. +# + +unset LD_LIBRARY_PATH +PATH=/usr/bin:/usr/sbin +export PATH + +. /lib/sdc/config.sh + +# subcommand: +# pre +# post + +# state +# ZONE_STATE_CONFIGURED 0 (script will never see this) +# ZONE_STATE_INCOMPLETE 1 (script will never see this) +# ZONE_STATE_INSTALLED 2 +# ZONE_STATE_READY 3 +# ZONE_STATE_RUNNING 4 +# ZONE_STATE_SHUTTING_DOWN 5 +# ZONE_STATE_DOWN 6 +# ZONE_STATE_MOUNTED 7 + +# cmd +# +# ready 0 +# boot 1 +# forceboot 2 +# reboot 3 +# halt 4 +# uninstalling 5 +# mount 6 +# forcemount 7 +# unmount 8 + +subcommand=$1 +ZONENAME=$2 +ZONEPATH=$3 +state=$4 +cmd=$5 + +VNDADM=/usr/sbin/vndadm +SNAPSHOT_DIR=root/checkpoints +OVERLAY_RULES=/var/run/smartdc/networking/overlay_rules.json +DEFAULT_MTU=1500 + +# +# The following are the list of features that a corresponding brand may +# enable. If they wish to do so, then they must set the following flags +# to values such that [[ -z $flag ]] is true. The following are the +# currently supported flags: +# +# o jst_vrrp - Enables vrrp +# o jst_ufpromisc - Supports unfiltered promiscuous mode +# o jst_createvnd - Create vnd devices +# o jst_simplefs - Only setup lastbooted in the FS +# o jst_showsnap - Show snapshots in the FS +# +# In addition, the brand must also specify the following parameters: +# +# o jst_mdatapath - The path the metadata socket is expected in the zone +# + +get_boolean_nic_property() +{ + bool_val=$(eval echo \$_ZONECFG_net_${1}_${2}) + if [[ "${bool_val}" == "1" ]] || [[ "${bool_val}" == "true" ]]; then + echo "true" + else + echo "false" + fi +} + +nm2prefix() +{ + prefix=0 + numparts=0 + OLDIFS=$IFS + IFS=. + for digit in $1 ; do + (( numparts+=1 )) + case $digit in + 255) + (( prefix+=8 )) + ;; + 254) + (( prefix+=7 )) + ;; + 252) + (( prefix+=6 )) + ;; + 248) + (( prefix+=5 )) + ;; + 240) + (( prefix+=4 )) + ;; + 224) + (( prefix+=3 )) + ;; + 192) + (( prefix+=2 )) + ;; + 128) + (( prefix+=1 )) + ;; + 0);; + *) + echo "Invalid digit in netmask: $digit" 1>&2; + IFS=$OLDIFS + return 1;; + esac + done + if [[ $numparts -ne 4 ]]; then + echo "Too many parts in the IP address" 1>&2; + IFS=$OLDIFS + return 1 + fi + IFS=$OLDIFS + echo "$prefix" +} + +# +# Set up the vnic(s) for the zone. +# +setup_net() +{ + typeset tmp overlay tag id rule + for nic in $_ZONECFG_net_resources + do + # Get simplified versions of the network config. variables. + address=$(eval echo \$_ZONECFG_net_${nic}_address) + # If address set, must be a shared stack zone + [[ -n $address ]] && exit 0 + + global_nic=$(eval echo \$_ZONECFG_net_${nic}_global_nic) + # If no global-nic, must be a dedicated physical NIC instead + # of a vnic + [[ -z $global_nic ]] && continue + + dhcp_server=$(get_boolean_nic_property ${nic} dhcp_server) + mac_addr=$(eval echo \$_ZONECFG_net_${nic}_mac_addr) + vlan_id=$(eval echo \$_ZONECFG_net_${nic}_vlan_id) + blocked_outgoing_ports=$(eval \ + echo \$_ZONECFG_net_${nic}_blocked_outgoing_ports) + zone_ips=$(eval echo \$_ZONECFG_net_${nic}_ips) + zone_ip=$(eval echo \$_ZONECFG_net_${nic}_ip) + zone_netmask=$(eval echo \$_ZONECFG_net_${nic}_netmask) + allow_dhcp_spoof=$(get_boolean_nic_property ${nic} allow_dhcp_spoofing) + allow_ip_spoof=$(get_boolean_nic_property ${nic} allow_ip_spoofing) + allow_mac_spoof=$(get_boolean_nic_property ${nic} allow_mac_spoofing) + allow_restricted_traffic=$(get_boolean_nic_property ${nic} \ + allow_restricted_traffic) + allow_unfiltered_promisc=$(get_boolean_nic_property ${nic} \ + allow_unfiltered_promisc) + allowed_ips=$(eval echo \$_ZONECFG_net_${nic}_allowed_ips) + allowed_dhcp_cids=$(eval echo \$_ZONECFG_net_${nic}_allowed_dhcp_cids) + vrid=$(eval echo \$_ZONECFG_net_${nic}_vrrp_vrid) + vrrp_primary_ip=$(eval \ + echo \$_ZONECFG_net_${nic}_vrrp_primary_ip) + mtu=$(eval echo \$_ZONECFG_net_${nic}_mtu) + isoverlay= + + # If we don't have our zone_ips, it may be because the configuration + # was made on an older platform. In that case, use the "ip" and + # "netmask" properties for this NIC, and save it as "ips". + if [[ -z $zone_ips && -n $zone_ip ]]; then + [[ -n $zone_netmask ]] && + zone_ip=$zone_ip/`nm2prefix $zone_netmask` + + zone_ips=$zone_ip + fi + + + + orig_global=$global_nic + + # + # The nic tag for a device (the zonecfg global_nic) can come in + # one of a few forms. It may: + # + # 1) Be a traditional tag which refers to a physical device or + # aggregation to create a VNIC over. The source of this + # mapping is sysinfo. + # + # 2) It can be the name of an etherstub. The source of these is + # from dladm show-etherstub + # + # 3) It can take the form of an overlay device rule. An overlay + # device rule is an invalid DLPI device and invalid nic tag. + # It has the form of <name>/<number>. For example, + # sdc_sdn/23. That refers to the overlay rule sdc_sdn. If we + # have an overlay rule, we may need to dynamically create the + # overlay device if it doesn't exist. + # + # To handle these cases, we first check if it's an overlay + # device, and then if not, check the other cases. + # + + tmp=$(echo $orig_global | sed -E 's_[a-zA-Z_0-9]+/[0-9]+__') + if [[ -n "$tmp" ]]; then + + # + # We only need sysinfo if we get here, and we only need to load it + # once. Loading is about the same cost as looking up a single + # value. + # + if [[ -z $SYSINFO_LOADED ]]; then + load_sdc_sysinfo + SYSINFO_LOADED="LOADED" + fi + global_nic=$(eval echo \$SYSINFO_NIC_${orig_global}) + + # If the global nic is specified as a device or etherstub name + # rather than a tag. + if [[ -z $global_nic ]]; then + echo "$(dladm show-phys -p -o LINK) $(dladm show-etherstub -p -o LINK)" \ + | egrep "(^| )${orig_global}( |$)" > /dev/null + (( $? == 0 )) && global_nic=${orig_global} + fi + else + isoverlay="true" + tag=${orig_global%/*} + num=${orig_global#*/} + global_nic="$tag$num" + rule=$(json -f $OVERLAY_RULES $tag) + if [[ $? -ne 0 || -z "$rule" ]]; then + logger -p daemon.err "zone $ZONENAME had tag " \ + "$tag which indicates an overlay rule, " \ + "no corresponding overlay rule found." + exit 1 + fi + fi + + # For backwards compatibility with the other parts of the + # system, check if this zone already has this vnic setup. + # If so, move on to the next vnic. + dladm show-vnic -p -o LINK -z $ZONENAME $nic >/dev/null 2>&1 + (( $? == 0 )) && continue + + if [[ -z $global_nic ]]; then + echo "undefined VNIC $nic " \ + "(global NIC $orig_global)" + logger -p daemon.err "zone $ZONENAME " \ + "undefined VNIC $nic (global NIC $orig_global)" + exit 1 + fi + + # + # If we have an overlay device, do we need to create it, or does + # it already exist? + # + if [[ -n "$isoverlay" ]]; then + if ! dladm show-overlay $global_nic 2>/dev/null; then + dladm create-overlay $rule -v $num $global_nic + if [[ $? -ne 0 ]]; then + # If creation fails, ALSO check + # for existence again, in case + # someone beat us to it. + if ! dladm show-overlay $global_nic \ + 2> /dev/null; then + logger -p daemon.err "zone $ZONENAME " \ + "failed to create overlay device " \ + "$global_nic with command " \ + "'dladm create-overlay $rule -v " \ + "$num $global_nic" + exit 1 + fi + fi + fi + fi + + + # + # Create the vnic. + # + + opt_str="-p " + + # + # Traditionally we created VNICs without ever specifying + # the MTU. In the world before we supported any kind of + # jumbo frames, this is fine, because it would always + # match the physical which was 1500 by default for + # almost all of our devices. However, when we added + # support for mtu in nictagadm and changing it in boot + # up, we didn't properly assert the default MTU. This + # has led to VMs potentially getting the wrong MTU and + # ending up using jumbo frames when the network is + # expecting 1500 byte frames. Marx Brothers-esque comedy + # and despair ensues. + # + # Thus we always assert that if no MTU is specified by + # the VM, then we go back to the traditional 'default' + # value which is 1500. + # + if [[ -z "$mtu" ]]; then + mtu=$DEFAULT_MTU + fi + + opt_str="$opt_str mtu=$mtu," + + # + # Always append the zone as the last property. This is + # to work around the fact that once we associate it with + # a zone, the zone will have a hold on the device and + # we'll not be able to delete it if a create fails due + # to a bad property (say an invalid MTU). Note if we + # have other properties, it is their responsibility to + # put a trailing comma on it. + # + opt_str="${opt_str}zone=$ZONENAME" + + if [[ -n "$jst_vrrp" && -n $vrid ]]; then + # MAC addresses for VRRP vnics are determined by the VRID + mac_addr="vrrp" + opt_str="$opt_str -V $vrid -A inet" + fi + + [[ -n $mac_addr ]] && opt_str="$opt_str -m $mac_addr" + + [[ -n $vlan_id && $vlan_id != 0 ]] && \ + opt_str="$opt_str -v $vlan_id" + + + # + # Creating a VNIC in a zone is a multi-step process internally. + # This means there is a short window where the VNIC exists in + # the global zone and that could lead to a race condition if + # two zones boot at the same time with the same VNIC name. Use + # a temp. name to create the VNIC then rename it to have the + # correct name. + # + tname=tmp$$0 + dout=`dladm create-vnic -t -l $global_nic $opt_str $tname 2>&1` + if (( $? != 0 )); then + printf "error creating VNIC %s (global NIC %s)\n" \ + "$nic" "$orig_global" + printf "msg: %s\n" "$dout" + printf "Failed cmd: dladm create-vnic %s" \ + "-t -l $global_nic $opt_str $tname" + logger -p daemon.err "zone $ZONENAME error creating " \ + "VNIC $nic (global NIC $orig_global $global_nic)" + logger -p daemon.err "msg: $dout" + logger -p daemon.err "Failed cmd: dladm create-vnic " \ + "-t -l $global_nic $opt_str $tname" + + # Show more info if dup MAC addr. + echo $dout | egrep -s "MAC address is already in use" + if (( $? == 0 )); then + entry=`dladm show-vnic -olink,macaddress,zone \ + | nawk -v addr=$mac_addr '{ + if ($2 == addr) + print $0 + }'` + if [[ -n $entry ]]; then + print -f "LINK\tMACADDRESS\tZONE\n" + print -f "%s\n" "$entry" + fi + fi + exit 1 + fi + dladm rename-link -z $ZONENAME $tname $nic + if (( $? != 0 )); then + echo "error renaming VNIC $tname $nic" + logger -p daemon.err "zone $ZONENAME error renaming " \ + "VNIC $tname $nic" + exit 1 + fi + + if [[ -z $mac_addr ]]; then + # There was no assigned mac address + + # Get newly assigned mac address. + mac_addr=$(dladm show-vnic -z $ZONENAME -p -o \ + MACADDRESS ${nic}) + + # Save newly assigned mac address + [[ -n $mac_addr ]] && zonecfg -z $ZONENAME \ + "select net physical=$nic; " \ + "set mac-addr=$mac_addr; end; exit" + fi + + # Set up antispoof options + + if [[ $dhcp_server == "true" ]] || [[ $allow_dhcp_spoof == "true" ]]; then + enable_dhcp="true" + # This needs to be off for dhcp server zones + allow_ip_spoof="true" + else + enable_dhcp="false" + fi + + comma="" + spoof_opts="" + if [[ $allow_mac_spoof != "true" ]]; then + spoof_opts="${spoof_opts}${comma}mac-nospoof" + comma="," + fi + if [[ $allow_ip_spoof != "true" ]]; then + spoof_opts="${spoof_opts}${comma}ip-nospoof" + comma="," + fi + if [[ $allow_restricted_traffic != "true" ]]; then + spoof_opts="${spoof_opts}${comma}restricted" + comma="," + fi + if [[ ${enable_dhcp} == "false" ]]; then + spoof_opts="${spoof_opts}${comma}dhcp-nospoof" + comma="," + fi + + if [[ -n ${spoof_opts} ]]; then + dladm set-linkprop -t -z $ZONENAME -p \ + "protection=${spoof_opts}" ${nic} + if (( $? != 0 )); then + echo "error setting VNIC protection $nic $spoof_opts" + logger -p daemon.err "zone $ZONENAME error setting " \ + "VNIC protection $nic $spoof_opts" + exit 1 + fi + fi + + # If we aren't using IP spoofing, we'll need to set the allowed-ips + # property on the NIC so that the zone will be able to ifconfig the + # proper addresses. + if [[ $allow_ip_spoof != "true" ]]; then + unset allowed_ip_map + typeset -A allowed_ip_map + + dynamic_methods="" + separator="" + OLDIFS=$IFS + IFS=, + + for zone_ip in $zone_ips; do + # For each static IP available, add it to the list. + if [[ $zone_ip == "dhcp" ]]; then + dynamic_methods+="${separator}dhcpv4" + separator="," + elif [[ $zone_ip == "addrconf" ]]; then + dynamic_methods+="${separator}addrconf" + separator="," + else + clean_ip=`printf "%s" "${zone_ip}" | sed 's|^\([^/]*\)/.*|\1|'` + allowed_ip_map[${clean_ip}]=true + fi + done + + # If any additional IPs have been specified (for example, older + # VMs set up for IPv6 before vmadm gained support), add them to + # the list. + for allowed_ip in $allowed_ips; do + allowed_ip_map[${allowed_ip}]=true + done + IFS=$OLDIFS + + # If we're using VRRP and have the IP, add it to the list. + if [[ -n "$jst_vrrp" && -n $vrrp_primary_ip ]]; then + allowed_ip_map[${vrrp_primary_ip}]=true + fi + + allowed_ip_list="" + + separator="" + for allowed_ip in ${!allowed_ip_map[@]}; do + allowed_ip_list+="${separator}${allowed_ip}" + separator="," + done + + # Set the allowed-ips property on the NIC + if [[ -n ${allowed_ip_list} ]] && + ! dladm set-linkprop -t -z $ZONENAME \ + -p "allowed-ips=${allowed_ip_list}" ${nic}; then + log_and_exit \ + "error setting VNIC allowed-ips $nic $allowed_ip_list" + fi + + # Set the dynamic-methods property on the NIC + if [[ -n ${dynamic_methods} ]] && + ! dladm set-linkprop -t -z $ZONENAME \ + -p "dynamic-methods=${dynamic_methods}" ${nic}; then + log_and_exit \ + "error setting VNIC dynamic-methods $nic $dynamic_methods" + fi + fi + + if [[ "$enable_dhcp" != "true" ]] && [[ -n "$allowed_dhcp_cids" ]] && + ! dladm set-linkprop -p "allowed-dhcp-cids=${allowed_dhcp_cids}" \ + -t -z $ZONENAME $nic; then + log_and_exit \ + "error setting VNIC allowed-dhcp-cids $nic $allowed_dhcp_cids" + fi + + if [[ "$enable_dhcp" != "true" ]] && [[ -z "$allowed_dhcp_cids" ]] && + [[ "$zone_ips" == *dhcp* || "$zone_ips" == *addrconf* ]] && + ! dladm set-linkprop -p "allow-all-dhcp-cids=true" \ + -t -z $ZONENAME $nic; then + log_and_exit "error setting VNIC allow-all-dhcp-cids $nic" + fi + + if [[ -n "$jst_ufpromisc" && ${allow_unfiltered_promisc} == "true" ]]; then + dladm set-linkprop -t -z $ZONENAME -p "promisc-filtered=off" ${nic} + fi + + if [[ -n $blocked_outgoing_ports ]]; then + OLDIFS=$IFS + IFS=, + for port in $blocked_outgoing_ports; do + # br='block remote'. Flow names should be < 31 + # chars in length so that they get unique + # kstats. + # Use the VNIC mac addr. to generate a unique + # name. + mac_addr=`dladm show-vnic -z $ZONENAME -p \ + -o MACADDRESS $nic | tr ':' '_'` + flowadm add-flow -t -l $nic -z $ZONENAME \ + -a transport=tcp,remote_port=$port \ + -p maxbw=0 f${mac_addr}_br_${port} + if (( $? != 0 )); then + echo "error adding flow " \ + "$nic f${mac_addr}_br_${port}" + logger -p daemon.err "zone $ZONENAME " \ + "error adding flow " \ + "$nic f${mac_addr}_br_${port}" + exit 1 + fi + done + IFS=$OLDIFS + fi + + if [[ -n "$jst_createvnd" ]]; then + # + # At this point we should go ahead and set up + # the vnd interface for this datalink. + # + $VNDADM create -z $ZONENAME $nic + if [[ $? -ne 0 ]]; then + echo "failed to create vnd device" + exit 1 + fi + fi + done +} + +# +# Log a message, then exit +# +log_and_exit() +{ + echo "$1" + logger -p daemon.err "zone $ZONENAME $1" + exit 1 +} + +# +# Set up the firewall for the zone. +# +setup_fw() +{ + ipf_conf=$ZONEPATH/config/ipf.conf + ipf6_conf=$ZONEPATH/config/ipf6.conf + if [ -e $ipf_conf ]; then + echo "starting firewall ($ipf_conf)" + /usr/sbin/ipf -GE $ZONENAME + if (( $? != 0 )); then + log_and_exit "error enabling ipfilter" + fi + + /usr/sbin/ipf -GFa $ZONENAME + if (( $? != 0 )); then + log_and_exit "error flushing ipfilter (IPv4)" + fi + + /usr/sbin/ipf -6GFa $ZONENAME + if (( $? != 0 )); then + log_and_exit "error flushing ipfilter (IPv6)" + fi + + /usr/sbin/ipf -Gf $ipf_conf $ZONENAME + if (( $? != 0 )); then + log_and_exit "error loading ipfilter config for IPv4" + fi + + if [[ -e $ipf6_conf ]] && + ! /usr/sbin/ipf -6Gf $ipf6_conf $ZONENAME; then + log_and_exit "error loading ipfilter config for IPv6" + fi + + /usr/sbin/ipf -Gy $ZONENAME + if (( $? != 0 )); then + log_and_exit "error syncing ipfilter interfaces" + fi + fi +} + +# +# We're readying the zone. Make sure the per-zone writable +# directories exist so that we can lofs mount them. We do this here, +# instead of in the install script, since this list has evolved and +# there are already zones out there in the installed state. +# +setup_fs() +{ + # create directory for metadata socket + mkdir -m755 -p /var/zonecontrol/${ZONENAME} + + uname -v > $ZONEPATH/lastbooted + [[ -n "$jst_simplefs" ]] && return + + [ ! -d $ZONEPATH/site ] && mkdir -m755 $ZONEPATH/site + [ ! -d $ZONEPATH/local ] && mkdir -m755 $ZONEPATH/local + [ ! -d $ZONEPATH/$SNAPSHOT_DIR ] && mkdir -m755 $ZONEPATH/$SNAPSHOT_DIR + if [ ! -d $ZONEPATH/ccs ]; then + mkdir -m755 $ZONEPATH/ccs + (cd /usr/ccs; tar cbf 512 - *) | \ + (cd $ZONEPATH/ccs; tar xbf 512 -) + fi + +} + +setup_snapshots() +{ + # + # Because the top-level directory of each ZFS snapshot contains some + # internal information, mount the /root directory of each snapshot + # separately. + # + for snap in $(ls -1 $ZONEPATH/.zfs/snapshot); do + snapdir=$ZONEPATH/$SNAPSHOT_DIR/$(echo ${snap} | sed -e "s/^vmsnap-//") + mkdir -p ${snapdir} + mount -F lofs -o ro,setuid,nodevices \ + $ZONEPATH/.zfs/snapshot/${snap}/root ${snapdir} + done +} + +# +# If the zone has a CPU cap, calculate the CPU baseline and set it so we can +# track when we're bursting. There are many ways that the baseline can be +# calculated based on the other settings in the zones (e.g. a simple way would +# be as a precentage of the cap). +# +# For SmartMachines, our CPU baseline is calculated off of the system's +# provisionable memory and the memory cap of the zone. We assume that 83% of +# the system's memory is usable by zones (the rest is for the OS) and we assume +# that the zone memory cap is set so that we're proportional to how many zones +# we can provision on the system (i.e. we don't overprovision memory). Using +# these assumptions, we calculate the proportion of CPU for the zone based on +# its proportion of memory. Thus, the zone's CPU baseline is calculated using: +# ((zone capped memsize in MB) * 100) / (MB/core). +# Uncapped zones have no baseline (i.e. infrastructure zones). +# +# Remember that the cpu-cap rctl and the baseline are expressed in units of +# a percent of a CPU, so 100 is 1 full CPU. +# +setup_cpu_baseline() +{ + # A brand can override the setup of bursting. + [ -n "$NO_BURSTING" ] && return + + # If there is already a baseline, don't set one heuristically + curr_base=`prctl -P -n zone.cpu-baseline -i zone $ZONENAME | nawk '{ + if ($2 == "privileged") print $3 + }'` + [ -n "$curr_base" ] && return + + # Get current cap and convert from zonecfg format into rctl format + cap=`zonecfg -z $ZONENAME info capped-cpu | nawk '{ + if ($1 == "[ncpus:") print (substr($2, 1, length($2) - 1) * 100) + }'` + [ -z "$cap" ] && return + + # Get zone's memory cap in MB times 100 + zmem=`zonecfg -z $ZONENAME info capped-memory | nawk '{ + if ($1 == "[physical:") { + val = substr($2, 1, length($2) - 2) + units = substr($2, length($2) - 1, 1) + + # convert GB to MB + if (units == "G") + val *= 1024 + print (val * 100) + } + }'` + [ -z "$zmem" ] && return + + # Get system's total memory in MB + smem=`prtconf -m` + # provisionable memory is 83% of total memory (bash can't do floats) + prov_mem=$((($smem * 83) / 100)) + nprocs=`psrinfo -v | \ + nawk '/virtual processor/ {cnt++} END {print cnt}'` + + mb_per_core=$(($prov_mem / $nprocs)) + + baseline=$(($zmem / $mb_per_core)) + [[ $baseline == 0 ]] && baseline=1 + [[ $baseline -gt $cap ]] && baseline=$cap + + prctl -n zone.cpu-baseline -v $baseline -t priv -i zone $ZONENAME +} + +cleanup_snapshots() +{ + # + # Each ZFS snapshot is mounted separately, so find all mounted + # snapshots for this zone, and unmount them. + # + snaps=$(ls -1 $ZONEPATH/$SNAPSHOT_DIR) + + for snap in ${snaps}; do + snapdir=$ZONEPATH/$SNAPSHOT_DIR/$(echo ${snap} | sed -e "s/^vmsnap-//") + umount ${snapdir} + rmdir ${snapdir} + done +} + +# +# We're halting the zone, perform network cleanup. +# +cleanup_net() +{ + # Cleanup any flows that were setup. + for nic in $_ZONECFG_net_resources + do + flowadm remove-flow -t -z $ZONENAME -l $nic + if (( $? != 0 )); then + echo "error removing flows for $nic" + logger -p daemon.err "zone $ZONENAME " \ + "error removing flows for $nic" + fi + done +} + +id_gz_sockholder() +{ + echo "searching for GZ process holding socket $1" + logger -p daemon.err "zone $ZONENAME " \ + "searching for GZ process holding socket $1" + + pid=`(cd /proc; + for i in *; + do + pfiles $i 2>/dev/null | egrep -s "AF_UNIX $1"; + [ $? == 0 ] && echo "$i"; + done)` + + [ -z "$pid" ] && return + + echo "Error: GZ process $pid holding socket $1 blocking shutdown" + logger -p daemon.err "Error: zone $ZONENAME:" \ + "GZ process $pid holding socket $1 blocking shutdown" +} + +# zonadmd unable to unmount the given path, try to cleanup so unmount can +# succeed. +cleanup_mount() +{ + echo "attempting to cleanup mount $1" + logger -p daemon.err "zone $ZONENAME attempting to cleanup mount $1" + + fnd_procs=0 + for i in `fuser -c $1 2>/dev/null` + do + fnd_procs=1 + + pty=`ps -otty -p $i | \ + nawk '{if ($1 != "TT" && $1 != "?") print $0}'` + + if [ -n "$pty" ]; then + echo "shell process $i blocking zone" \ + "$ZONENAME shutdown, killing the process" | wall + echo "killing GZ user shell $i under $1" + logger -p daemon.err "zone $ZONENAME:" \ + "killing GZ user shell $i under $1" + kill -9 $i + else + echo "Error: GZ process $i under $1 blocking shutdown" + logger -p daemon.err "Error: zone $ZONENAME:" \ + "GZ process $i under $1 blocking shutdown" + + local args="pargs: `pargs $i`" + echo "$args" + logger -p daemon.err "$args" + + local tree="ptree: `ptree $i`" + echo "$tree" + logger -p daemon.err "$tree" + fi + done + + if [ $fnd_procs -eq 1 ]; then + # Exit out to give the zoneadmd umount a chance to suceed now. + # Zoneadmd will give us another shot if it still can't umount. + sleep 1 + exit 0 + fi + + # Processes which are injected into a zone and then open a file as a + # socket end-point will show in pfiles with the path relative to the + # zone's root. For example, a zone with its root at /zones/foo/root and + # an open socket as /zones/foo/root/var/run/x will show up in a pfiles + # search as /var/run/x. This is a problem since we have no way to + # narrow down which process is the culprit. + # + # Because the socket doesn't have enough information for us to tie to + # the specific GZ process, we hardcode to id things we know will open + # sockets into the zone: + # $jst_mdatapath/metadata.sock + # /var/run/.smartdc-amon.sock + + ZVR=$ZONEPATH/root/var/run + [ -S $ZVR/smartdc/metadata.sock ] && + id_gz_sockholder $jst_mdatapath/metadata.sock + + [ -S $ZVR/.smartdc-amon.sock ] && + id_gz_sockholder /var/run/.smartdc-amon.sock +} + +function fix_forced_attrs { + typeset attr + + for attr in ${!FORCED_ATTRS[@]}; do + typeset nval=${FORCED_ATTRS["$attr"]} + typeset -n envvar=_ZONECFG_attr_${attr//-/_} + typeset cval=$envvar + + if [[ $cval == $nval ]]; then + # In most cases, $nval and $cval will be the same and + # nothing needs to be done. This includes the case where + # $nval and $cval are "". + continue + elif [[ -z $nval ]]; then + logger -p daemon.error "[zone $ZONENAME]" \ + "Illegal value for attr '$attr': '$cval'." \ + "Removing attr '$attr'" + zonecfg -z "$ZONENAME" "remove -F attr name=$attr" + + unset ${!envvar} + else + logger -p daemon.error "[zone $ZONENAME]" \ + "Illegal value for attr '$attr': '$cval'." \ + "Setting to '$nval'" + zonecfg -z "$ZONENAME" "remove -F attr name=$attr;" \ + "add attr; set type=string;" \ + "set name=$attr; set value=\"$nval\"; end;" + + export ${!envvar}="$nval" + fi + done +} + +# +# Main +# + +case $subcommand in +pre) + case $cmd in + 0) # pre-ready + fix_forced_attrs + setup_fs + ;; + 4) # pre-halt + [[ -n "$jst_showsnap" ]] && cleanup_snapshots + cleanup_net + ;; + esac + ;; +post) + case $cmd in + 0) # post-ready + [[ -n "$jst_showsnap" ]] && setup_snapshots + setup_net + setup_fw + ;; + 1) # post-boot + # We can't set a rctl until we have a process in the zone to + # grab + setup_cpu_baseline + ;; + 8) # post-unmount + # Zone halt is hung unmounting, try to recover + if [[ $state == 6 ]]; then + cleanup_mount "$6" + fi + ;; + esac + ;; +esac + +exit 0 diff --git a/usr/src/lib/brand/joyent-minimal/Makefile b/usr/src/lib/brand/joyent-minimal/Makefile new file mode 100644 index 0000000000..0823a1f94e --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/Makefile @@ -0,0 +1,31 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2019 Joyent, Inc. +# + +default: all + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS = zone + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber + +all install clean clobber: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/joyent-minimal/zone/Makefile b/usr/src/lib/brand/joyent-minimal/zone/Makefile new file mode 100644 index 0000000000..a0fbc14d5c --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/Makefile @@ -0,0 +1,37 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2019 Joyent, Inc. +# + +BRAND = joyent-minimal + +include $(SRC)/lib/brand/Makefile.brand + +PROGS = jattach jdetach jinstall juninstall poststate prestate statechange +XMLDOCS = config.xml platform.xml +TXTS = manifests + +CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTTXTS) $(ROOTPROGS) + +# These shouldn't be necessary, but for some reason the same thing in +# ../../Makefile.brand is not being picked up. +$(ROOTPROGS) := FILEMODE = 755 +$(ROOTTXTS) := FILEMODE = 444 +$(ROOTXMLDOCS) := FILEMODE = 444 + +all: $(PROGS) + +install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTXTS) + +clean: + $(RM) $(CLEANFILES) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/joyent-minimal/zone/config.xml b/usr/src/lib/brand/joyent-minimal/zone/config.xml new file mode 100644 index 0000000000..64e92464fe --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/config.xml @@ -0,0 +1,115 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2015 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN" + "file:///usr/share/lib/xml/dtd/brand.dtd.1"> + +<brand name="joyent-minimal"> + <modname></modname> + + <initname>/sbin/init</initname> + <login_cmd>/usr/bin/login -z %Z %u</login_cmd> + <forcedlogin_cmd>/usr/bin/login -z %Z -f %u</forcedlogin_cmd> + <user_cmd>/usr/bin/getent passwd %u</user_cmd> + + <install>/usr/lib/brand/joyent-minimal/jinstall -z %z -R %R</install> + <installopts>R:t:U:q:z:</installopts> + <boot></boot> + <halt></halt> + <verify_cfg></verify_cfg> + <verify_adm></verify_adm> + <postclone></postclone> + <postinstall></postinstall> + <attach>/usr/lib/brand/joyent-minimal/jattach -z %z -R %R</attach> + <detach>/usr/lib/brand/joyent-minimal/jdetach -z %z -R %R</detach> + <clone></clone> + <uninstall>/usr/lib/brand/joyent-minimal/juninstall -z %z -R %R</uninstall> + <prestatechange>/usr/lib/brand/joyent-minimal/prestate %z %R</prestatechange> + <poststatechange>/usr/lib/brand/joyent-minimal/poststate %z %R</poststatechange> + <query>/usr/lib/brand/joyent-minimal/query %z %R</query> + + <privilege set="default" name="contract_event" /> + <privilege set="default" name="contract_identity" /> + <privilege set="default" name="contract_observer" /> + <privilege set="default" name="dtrace_proc" /> + <privilege set="default" name="dtrace_user" /> + <privilege set="default" name="file_chown" /> + <privilege set="default" name="file_chown_self" /> + <privilege set="default" name="file_dac_execute" /> + <privilege set="default" name="file_dac_read" /> + <privilege set="default" name="file_dac_search" /> + <privilege set="default" name="file_dac_write" /> + <privilege set="default" name="file_owner" /> + <privilege set="default" name="file_setid" /> + <privilege set="default" name="ipc_dac_read" /> + <privilege set="default" name="ipc_dac_write" /> + <privilege set="default" name="ipc_owner" /> + <privilege set="default" name="net_bindmlp" /> + <privilege set="default" name="net_icmpaccess" /> + <privilege set="default" name="net_mac_aware" /> + <privilege set="default" name="net_observability" /> + <privilege set="default" name="net_privaddr" /> + <privilege set="default" name="net_rawaccess" ip-type="exclusive" /> + <privilege set="default" name="proc_chroot" /> + <privilege set="default" name="sys_audit" /> + <privilege set="default" name="proc_audit" /> + <privilege set="default" name="proc_lock_memory" /> + <privilege set="default" name="proc_owner" /> + <privilege set="default" name="proc_prioup" /> + <privilege set="default" name="proc_secflags" /> + <privilege set="default" name="proc_setid" /> + <privilege set="default" name="proc_taskid" /> + <privilege set="default" name="sys_acct" /> + <privilege set="default" name="sys_admin" /> + <privilege set="default" name="sys_fs_import" /> + <privilege set="default" name="sys_ip_config" ip-type="exclusive" /> + <privilege set="default" name="sys_iptun_config" ip-type="exclusive" /> + <privilege set="default" name="sys_mount" /> + <privilege set="default" name="sys_nfs" /> + <privilege set="default" name="sys_smb" /> + <privilege set="default" name="sys_resource" /> + <privilege set="default" name="sys_ppp_config" ip-type="exclusive" /> + + <privilege set="prohibited" name="dtrace_kernel" /> + <privilege set="prohibited" name="proc_zone" /> + <privilege set="prohibited" name="sys_config" /> + <privilege set="prohibited" name="sys_devices" /> + <privilege set="prohibited" name="sys_ip_config" ip-type="shared" /> + <privilege set="prohibited" name="sys_linkdir" /> + <privilege set="prohibited" name="sys_net_config" /> + <privilege set="prohibited" name="sys_res_config" /> + <privilege set="prohibited" name="sys_suser_compat" /> + <privilege set="prohibited" name="xvm_control" /> + <privilege set="prohibited" name="virt_manage" /> + <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" /> + + <privilege set="required" name="proc_exec" /> + <privilege set="required" name="proc_fork" /> + <privilege set="required" name="sys_ip_config" ip-type="exclusive" /> + <privilege set="required" name="sys_mount" /> +</brand> diff --git a/usr/src/lib/brand/joyent-minimal/zone/jattach b/usr/src/lib/brand/joyent-minimal/zone/jattach new file mode 100755 index 0000000000..b99c0211fe --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/jattach @@ -0,0 +1,26 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_zone_final_setup +} + +ps_brand="joyent-minimal" +. /usr/lib/brand/jcommon/cattach diff --git a/usr/src/lib/brand/joyent-minimal/zone/jdetach b/usr/src/lib/brand/joyent-minimal/zone/jdetach new file mode 100755 index 0000000000..5671f06bf9 --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/jdetach @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="joyent-minimal" +. /usr/lib/brand/jcommon/cdetach diff --git a/usr/src/lib/brand/joyent-minimal/zone/jinstall b/usr/src/lib/brand/joyent-minimal/zone/jinstall new file mode 100755 index 0000000000..167566bdca --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/jinstall @@ -0,0 +1,32 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# Does this brand support reprovisioning? +jst_reprovision="yes" + +# Is a template image optional? +# jst_tmplopt="yes" + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_zone_final_setup + cp /usr/lib/brand/joyent-minimal/repository.db ${ZROOT}/etc/svc +} + +. /usr/lib/brand/jcommon/cinstall diff --git a/usr/src/lib/brand/joyent-minimal/zone/juninstall b/usr/src/lib/brand/joyent-minimal/zone/juninstall new file mode 100755 index 0000000000..136044ad9e --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/juninstall @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +function jcommon_uninstall_hook +{ + : +} + +. /usr/lib/brand/jcommon/cuninstall diff --git a/usr/src/lib/brand/joyent-minimal/zone/manifests b/usr/src/lib/brand/joyent-minimal/zone/manifests new file mode 100644 index 0000000000..955d46702e --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/manifests @@ -0,0 +1,127 @@ +# +# This file lists the SMF manifests which are available in the global +# zone and which should be setup for use in non-global zones. Not +# all SMF services are usable within a zone, so only those that are usable +# should be listed here. +# +# The format of the file is: +# manifest_name enable/disable +# The manifest_name is found relative to /lib/svc/manifest on the system. +# The second field is used to indicate if the svc should be enabled or +# disabled by default within zones, independently of its current status in +# the global zone. +# +# The following is a list of SMF svcs which could be run in a zone, but +# which we're not currently installing on our live image. If we add any +# of these to the image and we want it to also be available in zones, +# then we need to move the entry to the end and uncomment it out. +# +# application/cups.xml +# application/font/fc-cache.xml +# application/graphical-login/gdm.xml +# application/management/net-snmp.xml +# application/management/seaport.xml +# application/management/snmpdx.xml +# application/opengl/ogl-select.xml +# application/print/service-selector.xml +# application/security/tcsd.xml +# application/x11/x11-server.xml +# application/x11/xfs.xml +# application/x11/xvnc-inetd.xml +# network/ldap/client.xml +# network/rpc/keyserv.xml +# network/socket-filter-kssl.xml +# network/ssl/kssl-proxy.xml +# network/nfs/rquota.xml +# network/smb/server.xml +# network/dns/server.xml +# network/ftp.xml +# network/finger.xml +# network/comsat.xml +# network/rpc/rstat.xml +# network/rpc/rusers.xml +# network/rpc/spray.xml +# network/rpc/wall.xml +# network/talk.xml +# network/ntp.xml +# network/sendmail-client.xml +# network/smtp-sendmail.xml +# network/telnet.xml +# network/wpa.xml +# network/rpc/gss.xml +# network/security/kadmin.xml +# network/security/krb5kdc.xml +# network/security/ktkt_warn.xml +# network/rpc/smserver.xml +# network/nfs/cbd.xml +# network/nfs/mapid.xml +# network/nfs/status.xml +# network/ipmievd.xml +# network/nis/client.xml +# network/http-apache22.xml +# system/consolekit.xml +# system/device/devices-audio.xml +# system/fm/notify-params.xml +# system/install/system-config.xml +# system/pkgserv.xml +# + +# +# The following is a list of SMF svcs which are installed on our live image and +# which could be run in a zone, but which we choose not to run in our zones. +# If we ever do want to run any of these, move the entry to the end and +# uncomment it out. +# +# network/nfs/client.xml +# network/nfs/nlockmgr.xml +# system/boot-archive-update.xml +# system/boot-archive.xml +# system/boot-config.xml +# system/cron.xml +# system/fmd.xml +# system/sac.xml +# + +# +# The following is a list of SMF svc manifests under /lib/svc/manifest. These +# are available on our live image and are runnable in a zone. Since the zones +# are sharing the base file system with the global zone, we set up so that +# only these svcs are configured in zones, independently of the global zone +# /lib/svc/manifest hierarchy. +# + +system/svc/restarter.xml enabled +network/dlmgmt.xml enabled +system/filesystem/root-fs.xml enabled +# +# This was running, but not in our set of manifests. Maybe it came in +# from /var. +# +system/boot-archive.xml enabled +network/network-ipmgmt.xml enabled +system/filesystem/usr-fs.xml enabled +network/network-loopback.xml enabled +system/device/devices-local.xml enabled +milestone/single-user.xml enabled +network/network-physical.xml enabled +system/filesystem/minimal-fs.xml enabled +milestone/network.xml enabled +system/filesystem/joyent-fs.xml enabled +system/filesystem/local-fs.xml enabled +system/name-service-cache.xml enabled +system/process-security.xml enabled +milestone/name-services.xml enabled +system/identity.xml enabled +network/network-initial.xml enabled +network/network-service.xml enabled +network/dns/client.xml enabled +network/network-netmask.xml enabled +network/network-routing-setup.xml enabled +milestone/sysconfig.xml enabled +system/system-log.xml enabled +milestone/multi-user-server.xml enabled +milestone/multi-user.xml enabled +milestone/name-services.xml enabled +system/early-manifest-import.xml disabled +system/manifest-import.xml disabled +system/mdata.xml enabled diff --git a/usr/src/lib/brand/joyent-minimal/zone/platform.xml b/usr/src/lib/brand/joyent-minimal/zone/platform.xml new file mode 100644 index 0000000000..f227b409b3 --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/platform.xml @@ -0,0 +1,172 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2017 Joyent, Inc. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN" + "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1"> + +<platform name="joyent-minimal" allow-exclusive-ip="true"> + + <!-- Global filesystems to mount when booting the zone --> + <global_mount special="/dev" directory="/dev" type="dev" + opt="attrdir=%R/root/dev"/> + + <global_mount special="/var/zonecontrol/%z" directory="/.zonecontrol" + opt="ro,nodevices,nosetuid,noexec" type="lofs" /> + + <global_mount special="/lib" directory="/lib" + opt="ro,nodevices" type="lofs" /> + <global_mount special="%P/manifests/joyent" directory="/lib/svc/manifest" + opt="ro,nodevices" type="lofs" /> + <global_mount special="%R/site" directory="/lib/svc/manifest/site" + opt="nodevices" type="lofs" /> + <global_mount special="/sbin" directory="/sbin" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/usr" directory="/usr" + opt="ro,nodevices" type="lofs" /> + <global_mount special="%R/local" directory="/usr/local" + opt="nodevices" type="lofs" /> + <global_mount special="%R/ccs" directory="/usr/ccs" + opt="nodevices" type="lofs" /> + <mount special="sharefs" directory="/etc/dfs/sharetab" type="sharefs" /> + + <!-- Local filesystems to mount when booting the zone --> + <mount special="/proc" directory="/proc" type="proc" /> + <mount special="ctfs" directory="/system/contract" type="ctfs" /> + <mount special="mnttab" directory="/etc/mnttab" type="mntfs" /> + <mount special="objfs" directory="/system/object" type="objfs" /> + <mount special="lxproc" directory="/system/lxproc" type="lxproc" /> + <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" /> + + <!-- Devices to create under /dev --> + <device match="arp" /> + <device match="bpf" /> + <device match="conslog" /> + <device match="cpu/self/cpuid" /> + <device match="crypto" /> + <device match="cryptoadm" /> + <device match="dsk" /> + <device match="dtrace/*" /> + <device match="dtrace/provider/*" /> + <device match="eventfd" /> + <device match="fd" /> + <device match="full" /> + <device match="inotify" /> + <device match="ipnet" /> + <device match="kstat" /> + <device match="lo0" /> + <device match="lofictl" /> + <device match="lofi" /> + <device match="log" /> + <device match="logindmux" /> + <device match="nsmb" /> + <device match="net/*" /> + <device match="null" /> + <device match="openprom" arch="sparc" /> + <device match="poll" /> + <device match="pool" /> + <device match="ptmx" /> + <device match="pts/*" /> + <device match="random" /> + <device match="rdsk" /> + <device match="rlofi" /> + <device match="rmt" /> + <device match="sad/user" /> + <device match="smbsrv" /> + <device match="signalfd" /> + <device match="svvslo0" /> + <device match="svvslo1" /> + <device match="svvslo2" /> + <device match="svvslo3" /> + <device match="swap" /> + <device match="sysevent" /> + <device match="tap" /> + <device match="tcp" /> + <device match="tcp6" /> + <device match="term" /> + <device match="ticlts" /> + <device match="ticots" /> + <device match="ticotsord" /> + <device match="timerfd" /> + <device match="tty" /> + <device match="tun" /> + <device match="udp" /> + <device match="udp6" /> + <device match="urandom" /> + <device match="vnd/*" /> + <device match="zero" /> + <device match="zfd/%z/slave/0" name="zfd/0" /> + <device match="zfd/%z/slave/1" name="zfd/1" /> + <device match="zfd/%z/slave/2" name="zfd/2" /> + <device match="zfs" /> + <device match="zvol/dsk/%P/%z/*" /> + <device match="zvol/rdsk/%P/%z/*" /> + + <!-- Devices to create in exclusive IP zone only --> + <device match="dld" ip-type="exclusive" /> + <device match="icmp" ip-type="exclusive" /> + <device match="icmp6" ip-type="exclusive" /> + <device match="ip" ip-type="exclusive" /> + <device match="ip6" ip-type="exclusive" /> + <device match="ipauth" ip-type="exclusive" /> + <device match="ipd" ip-type="exclusive" /> + <device match="ipf" ip-type="exclusive" /> + <device match="ipl" ip-type="exclusive" /> + <device match="iplookup" ip-type="exclusive" /> + <device match="ipmpstub" ip-type="exclusive" /> + <device match="ipnat" ip-type="exclusive" /> + <device match="ipscan" ip-type="exclusive" /> + <device match="ipsecah" ip-type="exclusive" /> + <device match="ipsecesp" ip-type="exclusive" /> + <device match="ipstate" ip-type="exclusive" /> + <device match="ipsync" ip-type="exclusive" /> + <device match="keysock" ip-type="exclusive" /> + <device match="rawip" ip-type="exclusive" /> + <device match="rawip6" ip-type="exclusive" /> + <device match="rts" ip-type="exclusive" /> + <device match="sad/admin" ip-type="exclusive" /> + <device match="sctp" ip-type="exclusive" /> + <device match="sctp6" ip-type="exclusive" /> + <device match="spdsock" ip-type="exclusive" /> + <device match="sppp" ip-type="exclusive" /> + <device match="sppptun" ip-type="exclusive" /> + <device match="vni" ip-type="exclusive" /> + + <!-- Renamed devices to create under /dev --> + <device match="zcons/%z/zoneconsole" name="zconsole" /> + + <!-- Symlinks to create under /dev --> + <symlink source="console" target="zconsole" /> + <symlink source="dtremote" target="/dev/null" /> + <symlink source="msglog" target="zconsole" /> + <symlink source="stderr" target="./fd/2" /> + <symlink source="stdin" target="./fd/0" /> + <symlink source="stdout" target="./fd/1" /> + <symlink source="syscon" target="zconsole" /> + <symlink source="sysmsg" target="zconsole" /> + <symlink source="systty" target="zconsole" /> + +</platform> diff --git a/usr/src/lib/brand/joyent-minimal/zone/poststate b/usr/src/lib/brand/joyent-minimal/zone/poststate new file mode 100755 index 0000000000..be489ef55b --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/poststate @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="joyent-minimal" +. /usr/lib/brand/jcommon/poststate diff --git a/usr/src/lib/brand/joyent-minimal/zone/prestate b/usr/src/lib/brand/joyent-minimal/zone/prestate new file mode 100755 index 0000000000..e6aeb1cba5 --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/prestate @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="joyent-minimal" +. /usr/lib/brand/jcommon/prestate diff --git a/usr/src/lib/brand/joyent-minimal/zone/statechange b/usr/src/lib/brand/joyent-minimal/zone/statechange new file mode 100755 index 0000000000..5e2eeff0f2 --- /dev/null +++ b/usr/src/lib/brand/joyent-minimal/zone/statechange @@ -0,0 +1,42 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018 Joyent, Inc. All rights reserved. +# + +# Do we support vrrp? +jst_vrrp="yes" + +# Do we support unfiltered promiscuous mode +# jst_ufpromisc="yes" + +# Do we use vnd devices +# jst_createvnd="yes" + +# Do we not need to have various SMF and ccs stuff created for us? +# jst_simplefs="yes" + +# Should we show snapshots? +jst_showsnap="yes" + +# Where does the mdata socket live? +jst_mdatapath="/.zonecontrol/" + +# Ensure docker zfd(7D) log is not put in the wrong place +if [[ "$_ZONECFG_attr_docker" == true ]]; then + typeset -A FORCED_ATTRS + FORCED_ATTRS["zlog-name"]= +fi + +. /usr/lib/brand/jcommon/statechange diff --git a/usr/src/lib/brand/joyent/Makefile b/usr/src/lib/brand/joyent/Makefile new file mode 100644 index 0000000000..0823a1f94e --- /dev/null +++ b/usr/src/lib/brand/joyent/Makefile @@ -0,0 +1,31 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2019 Joyent, Inc. +# + +default: all + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS = zone + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber + +all install clean clobber: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/joyent/zone/Joyent.xml b/usr/src/lib/brand/joyent/zone/Joyent.xml new file mode 100644 index 0000000000..cbb810301e --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/Joyent.xml @@ -0,0 +1,32 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2010 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + DO NOT EDIT THIS FILE. Use zonecfg(1M) instead. +--> + +<!DOCTYPE zone PUBLIC "-//Joyent Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1"> + +<zone name="default" zonepath="" autoboot="false" brand="joyent"> +</zone> diff --git a/usr/src/lib/brand/joyent/zone/Makefile b/usr/src/lib/brand/joyent/zone/Makefile new file mode 100644 index 0000000000..3ffc11c8a0 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/Makefile @@ -0,0 +1,39 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2019 Joyent, Inc. +# + +BRAND = joyent + +include $(SRC)/lib/brand/Makefile.brand + +PROGS = jattach jdetach jinstall juninstall poststate prestate statechange +XMLDOCS = config.xml platform.xml +TEMPLATES = Joyent.xml SUNWdefault.xml +TXTS = manifests + +CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTTXTS) $(ROOTPROGS) $(ROOTTEMPLATES) + +# These shouldn't be necessary, but for some reason the same thing in +# ../../Makefile.brand is not being picked up. +$(ROOTPROGS) := FILEMODE = 755 +$(ROOTTXTS) := FILEMODE = 444 +$(ROOTXMLDOCS) := FILEMODE = 444 +$(ROOTTEMPLATES) := FILEMODE = 444 + +all: $(PROGS) + +install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTXTS) $(ROOTTEMPLATES) + +clean: + $(RM) $(CLEANFILES) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/ipkg/zone/SUNWdefault.xml b/usr/src/lib/brand/joyent/zone/SUNWdefault.xml index 8c1079256b..070412f6c1 100644 --- a/usr/src/lib/brand/ipkg/zone/SUNWdefault.xml +++ b/usr/src/lib/brand/joyent/zone/SUNWdefault.xml @@ -28,5 +28,5 @@ <!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1"> -<zone name="default" zonepath="" autoboot="false" brand="ipkg"> +<zone name="default" zonepath="" autoboot="false" brand="joyent"> </zone> diff --git a/usr/src/lib/brand/joyent/zone/config.xml b/usr/src/lib/brand/joyent/zone/config.xml new file mode 100644 index 0000000000..c6fdb574dd --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/config.xml @@ -0,0 +1,115 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2015 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN" + "file:///usr/share/lib/xml/dtd/brand.dtd.1"> + +<brand name="joyent"> + <modname></modname> + + <initname>/sbin/init</initname> + <login_cmd>/usr/bin/login -z %Z %u</login_cmd> + <forcedlogin_cmd>/usr/bin/login -z %Z -f %u</forcedlogin_cmd> + <user_cmd>/usr/bin/getent passwd %u</user_cmd> + + <install>/usr/lib/brand/joyent/jinstall -z %z -R %R</install> + <installopts>R:t:U:q:z:</installopts> + <boot></boot> + <halt></halt> + <verify_cfg></verify_cfg> + <verify_adm></verify_adm> + <postclone></postclone> + <postinstall></postinstall> + <attach>/usr/lib/brand/joyent/jattach -z %z -R %R</attach> + <detach>/usr/lib/brand/joyent/jdetach -z %z -R %R</detach> + <clone></clone> + <uninstall>/usr/lib/brand/joyent/juninstall -z %z -R %R</uninstall> + <prestatechange>/usr/lib/brand/joyent/prestate %z %R</prestatechange> + <poststatechange>/usr/lib/brand/joyent/poststate %z %R</poststatechange> + <query>/usr/lib/brand/joyent/query %z %R</query> + + <privilege set="default" name="contract_event" /> + <privilege set="default" name="contract_identity" /> + <privilege set="default" name="contract_observer" /> + <privilege set="default" name="dtrace_proc" /> + <privilege set="default" name="dtrace_user" /> + <privilege set="default" name="file_chown" /> + <privilege set="default" name="file_chown_self" /> + <privilege set="default" name="file_dac_execute" /> + <privilege set="default" name="file_dac_read" /> + <privilege set="default" name="file_dac_search" /> + <privilege set="default" name="file_dac_write" /> + <privilege set="default" name="file_owner" /> + <privilege set="default" name="file_setid" /> + <privilege set="default" name="ipc_dac_read" /> + <privilege set="default" name="ipc_dac_write" /> + <privilege set="default" name="ipc_owner" /> + <privilege set="default" name="net_bindmlp" /> + <privilege set="default" name="net_icmpaccess" /> + <privilege set="default" name="net_mac_aware" /> + <privilege set="default" name="net_observability" /> + <privilege set="default" name="net_privaddr" /> + <privilege set="default" name="net_rawaccess" ip-type="exclusive" /> + <privilege set="default" name="proc_chroot" /> + <privilege set="default" name="sys_audit" /> + <privilege set="default" name="proc_audit" /> + <privilege set="default" name="proc_lock_memory" /> + <privilege set="default" name="proc_owner" /> + <privilege set="default" name="proc_prioup" /> + <privilege set="default" name="proc_secflags" /> + <privilege set="default" name="proc_setid" /> + <privilege set="default" name="proc_taskid" /> + <privilege set="default" name="sys_acct" /> + <privilege set="default" name="sys_admin" /> + <privilege set="default" name="sys_fs_import" /> + <privilege set="default" name="sys_ip_config" ip-type="exclusive" /> + <privilege set="default" name="sys_iptun_config" ip-type="exclusive" /> + <privilege set="default" name="sys_mount" /> + <privilege set="default" name="sys_nfs" /> + <privilege set="default" name="sys_smb" /> + <privilege set="default" name="sys_resource" /> + <privilege set="default" name="sys_ppp_config" ip-type="exclusive" /> + + <privilege set="prohibited" name="dtrace_kernel" /> + <privilege set="prohibited" name="proc_zone" /> + <privilege set="prohibited" name="sys_config" /> + <privilege set="prohibited" name="sys_devices" /> + <privilege set="prohibited" name="sys_ip_config" ip-type="shared" /> + <privilege set="prohibited" name="sys_linkdir" /> + <privilege set="prohibited" name="sys_net_config" /> + <privilege set="prohibited" name="sys_res_config" /> + <privilege set="prohibited" name="sys_suser_compat" /> + <privilege set="prohibited" name="xvm_control" /> + <privilege set="prohibited" name="virt_manage" /> + <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" /> + + <privilege set="required" name="proc_exec" /> + <privilege set="required" name="proc_fork" /> + <privilege set="required" name="sys_ip_config" ip-type="exclusive" /> + <privilege set="required" name="sys_mount" /> +</brand> diff --git a/usr/src/lib/brand/joyent/zone/jattach b/usr/src/lib/brand/joyent/zone/jattach new file mode 100755 index 0000000000..11844b9ac4 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/jattach @@ -0,0 +1,26 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_zone_final_setup +} + +ps_brand="joyent" +. /usr/lib/brand/jcommon/cattach diff --git a/usr/src/lib/brand/joyent/zone/jdetach b/usr/src/lib/brand/joyent/zone/jdetach new file mode 100755 index 0000000000..89681919f1 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/jdetach @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="joyent" +. /usr/lib/brand/jcommon/cdetach diff --git a/usr/src/lib/brand/joyent/zone/jinstall b/usr/src/lib/brand/joyent/zone/jinstall new file mode 100755 index 0000000000..8e6ec2a7b2 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/jinstall @@ -0,0 +1,31 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# Does this brand support reprovisioning? +jst_reprovision="yes" + +# Is a template image optional? +# jst_tmplopt="yes" + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_zone_final_setup +} + +. /usr/lib/brand/jcommon/cinstall diff --git a/usr/src/lib/brand/joyent/zone/juninstall b/usr/src/lib/brand/joyent/zone/juninstall new file mode 100755 index 0000000000..136044ad9e --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/juninstall @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +function jcommon_uninstall_hook +{ + : +} + +. /usr/lib/brand/jcommon/cuninstall diff --git a/usr/src/lib/brand/joyent/zone/manifests b/usr/src/lib/brand/joyent/zone/manifests new file mode 100644 index 0000000000..684f4c9c4b --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/manifests @@ -0,0 +1,177 @@ +# +# This file lists the SMF manifests which are available in the global +# zone and which should be setup for use in non-global zones. Not +# all SMF services are usable within a zone, so only those that are usable +# should be listed here. +# +# The format of the file is: +# manifest_name enable/disable +# The manifest_name is found relative to /lib/svc/manifest on the system. +# The second field is used to indicate if the svc should be enabled or +# disabled by default within zones, independently of its current status in +# the global zone. +# +# The following is a list of SMF svcs which could be run in a zone, but +# which we're not currently installing on our live image. If we add any +# of these to the image and we want it to also be available in zones, +# then we need to move the entry to the end and uncomment it out. +# +# application/cups.xml +# application/font/fc-cache.xml +# application/graphical-login/gdm.xml +# application/management/net-snmp.xml +# application/management/seaport.xml +# application/management/snmpdx.xml +# application/opengl/ogl-select.xml +# application/print/service-selector.xml +# application/security/tcsd.xml +# application/x11/x11-server.xml +# application/x11/xfs.xml +# application/x11/xvnc-inetd.xml +# network/rpc/keyserv.xml +# network/socket-filter-kssl.xml +# network/ssl/kssl-proxy.xml +# network/dns/server.xml +# network/ftp.xml +# network/finger.xml +# network/comsat.xml +# network/rpc/rstat.xml +# network/rpc/rusers.xml +# network/rpc/spray.xml +# network/rpc/wall.xml +# network/talk.xml +# network/ntp.xml +# network/sendmail-client.xml +# network/smtp-sendmail.xml +# network/telnet.xml +# network/wpa.xml +# network/rpc/gss.xml +# network/security/kadmin.xml +# network/security/krb5kdc.xml +# network/security/ktkt_warn.xml +# network/rpc/smserver.xml +# network/nfs/cbd.xml +# network/ipmievd.xml +# network/http-apache22.xml +# system/consolekit.xml +# system/device/devices-audio.xml +# system/fm/notify-params.xml +# system/install/system-config.xml +# system/pkgserv.xml +# + +# +# The following is a list of SMF svcs which are installed on our live image and +# which could be run in a zone, but which we choose not to run in our zones. +# If we ever do want to run any of these, move the entry to the end and +# uncomment it out. +# +# system/boot-archive-update.xml +# system/boot-config.xml +# + +# +# The following is a list of SMF svc manifests under /lib/svc/manifest. These +# are available on our live image and are runnable in a zone. Since the zones +# are sharing the base file system with the global zone, we set up so that +# only these svcs are configured in zones, independently of the global zone +# /lib/svc/manifest hierarchy. +# +milestone/multi-user-server.xml enabled +milestone/multi-user.xml enabled +milestone/name-services.xml enabled +milestone/network.xml enabled +milestone/single-user.xml enabled +milestone/sysconfig.xml enabled +network/bridge.xml enabled +network/dlmgmt.xml enabled +network/dns/client.xml enabled +network/dns/install.xml disabled +network/dns/multicast.xml disabled +network/forwarding.xml disabled +network/inetd-upgrade.xml enabled +network/inetd.xml disabled +network/ipfilter.xml disabled +network/ipsec/ike.xml disabled +network/ipsec/ipsecalgs.xml enabled +network/ipsec/manual-key.xml disabled +network/ipsec/policy.xml enabled +network/ldap/client.xml disabled +network/loadbalancer/ilbd.xml disabled +network/login.xml enabled +network/network-initial.xml enabled +network/network-install.xml disabled +network/network-ipmgmt.xml enabled +network/network-ipqos.xml disabled +network/network-iptun.xml enabled +network/network-location.xml disabled +network/network-loopback.xml enabled +network/network-netcfg.xml enabled +network/network-netmask.xml enabled +network/network-physical.xml enabled +network/network-routing-setup.xml enabled +network/network-service.xml enabled +network/nfs/client.xml disabled +network/nfs/mapid.xml disabled +network/nfs/nlockmgr.xml disabled +network/nfs/rquota.xml disabled +network/nfs/status.xml disabled +network/nfs/server.xml disabled +network/nis/client.xml disabled +network/rexec.xml disabled +network/routing/legacy-routing.xml enabled +network/routing/ndp.xml disabled +network/routing/rdisc.xml disabled +network/routing/ripng.xml disabled +network/routing/route.xml disabled +network/rpc/bind.xml enabled +network/shares/group.xml enabled +network/shares/reparsed.xml disabled +network/shell.xml disabled +network/slp.xml disabled +network/smb/client.xml disabled +network/smb/server.xml disabled +network/ssh.xml enabled +network/vrrp.xml disabled +system/auditd.xml disabled +system/auditset.xml disabled +system/boot-archive.xml enabled +system/consadm.xml disabled +system/console-login.xml disabled +system/coreadm.xml enabled +system/cron.xml enabled +system/cryptosvc.xml enabled +system/device/allocate.xml disabled +system/device/devices-local.xml enabled +system/device/mpxio-upgrade.xml disabled +system/early-manifest-import.xml enabled +system/extended-accounting.xml enabled +system/filesystem/autofs.xml disabled +system/filesystem/joyent-fs.xml enabled +system/filesystem/local-fs.xml enabled +system/filesystem/minimal-fs.xml enabled +system/filesystem/root-fs.xml enabled +system/filesystem/usr-fs.xml enabled +system/fmd.xml disabled +system/hostid.xml enabled +system/hotplug.xml disabled +system/identity.xml enabled +system/idmap.xml disabled +system/keymap.xml enabled +system/logadm-upgrade.xml enabled +system/manifest-import.xml enabled +system/mdata.xml disabled +system/name-service-cache.xml enabled +system/pfexecd.xml enabled +system/process-security.xml enabled +system/rbac.xml enabled +system/rcap.xml disabled +system/rmtmpfiles.xml enabled +system/sac.xml disabled +system/sar.xml disabled +system/svc/global.xml disabled +system/svc/restarter.xml enabled +system/sysidtool.xml enabled +system/system-log.xml enabled +system/utmp.xml enabled +system/vtdaemon.xml disabled diff --git a/usr/src/lib/brand/joyent/zone/platform.xml b/usr/src/lib/brand/joyent/zone/platform.xml new file mode 100644 index 0000000000..f07f2378e7 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/platform.xml @@ -0,0 +1,169 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2017 Joyent, Inc. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN" + "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1"> + +<platform name="joyent" allow-exclusive-ip="true"> + + <!-- Global filesystems to mount when booting the zone --> + <global_mount special="/dev" directory="/dev" type="dev" + opt="attrdir=%R/root/dev"/> + + <global_mount special="/var/zonecontrol/%z" directory="/.zonecontrol" + opt="ro,nodevices,nosetuid,noexec" type="lofs" /> + <global_mount special="/lib" directory="/lib" + opt="ro,nodevices" type="lofs" /> + <global_mount special="%P/manifests/joyent" + directory="/lib/svc/manifest" + opt="ro,nodevices" type="lofs" /> + <global_mount special="%R/site" directory="/lib/svc/manifest/site" + opt="nodevices" type="lofs" /> + <global_mount special="/sbin" directory="/sbin" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/usr" directory="/usr" + opt="ro,nodevices" type="lofs" /> + <global_mount special="%R/local" directory="/usr/local" + opt="nodevices" type="lofs" /> + <global_mount special="%R/ccs" directory="/usr/ccs" + opt="nodevices" type="lofs" /> + + <!-- Local filesystems to mount when booting the zone --> + <mount special="/proc" directory="/proc" type="proc" /> + <mount special="ctfs" directory="/system/contract" type="ctfs" /> + <mount special="mnttab" directory="/etc/mnttab" type="mntfs" /> + <mount special="objfs" directory="/system/object" type="objfs" /> + <mount special="lxproc" directory="/system/lxproc" type="lxproc" /> + <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" /> + <mount special="sharefs" directory="/etc/dfs/sharetab" type="sharefs" /> + + <!-- Devices to create under /dev --> + <device match="arp" /> + <device match="bpf" /> + <device match="conslog" /> + <device match="cpu/self/cpuid" /> + <device match="crypto" /> + <device match="cryptoadm" /> + <device match="dsk" /> + <device match="dtrace/*" /> + <device match="dtrace/provider/*" /> + <device match="eventfd" /> + <device match="fd" /> + <device match="full" /> + <device match="inotify" /> + <device match="ipnet" /> + <device match="kstat" /> + <device match="lo0" /> + <device match="lofictl" /> + <device match="lofi" /> + <device match="log" /> + <device match="logindmux" /> + <device match="nsmb" /> + <device match="net/*" /> + <device match="null" /> + <device match="openprom" arch="sparc" /> + <device match="poll" /> + <device match="pool" /> + <device match="ptmx" /> + <device match="pts/*" /> + <device match="random" /> + <device match="rdsk" /> + <device match="rlofi" /> + <device match="rmt" /> + <device match="sad/user" /> + <device match="smbsrv" /> + <device match="signalfd" /> + <device match="svvslo0" /> + <device match="svvslo1" /> + <device match="svvslo2" /> + <device match="svvslo3" /> + <device match="swap" /> + <device match="sysevent" /> + <device match="tap" /> + <device match="tcp" /> + <device match="tcp6" /> + <device match="term" /> + <device match="ticlts" /> + <device match="ticots" /> + <device match="ticotsord" /> + <device match="timerfd" /> + <device match="tty" /> + <device match="tun" /> + <device match="udp" /> + <device match="udp6" /> + <device match="urandom" /> + <device match="vnd/*" /> + <device match="zero" /> + <device match="zfs" /> + <device match="zvol/dsk/%P/%z/*" /> + <device match="zvol/rdsk/%P/%z/*" /> + + <!-- Devices to create in exclusive IP zone only --> + <device match="dld" ip-type="exclusive" /> + <device match="icmp" ip-type="exclusive" /> + <device match="icmp6" ip-type="exclusive" /> + <device match="ip" ip-type="exclusive" /> + <device match="ip6" ip-type="exclusive" /> + <device match="ipauth" ip-type="exclusive" /> + <device match="ipd" ip-type="exclusive" /> + <device match="ipf" ip-type="exclusive" /> + <device match="ipl" ip-type="exclusive" /> + <device match="iplookup" ip-type="exclusive" /> + <device match="ipmpstub" ip-type="exclusive" /> + <device match="ipnat" ip-type="exclusive" /> + <device match="ipscan" ip-type="exclusive" /> + <device match="ipsecah" ip-type="exclusive" /> + <device match="ipsecesp" ip-type="exclusive" /> + <device match="ipstate" ip-type="exclusive" /> + <device match="ipsync" ip-type="exclusive" /> + <device match="keysock" ip-type="exclusive" /> + <device match="rawip" ip-type="exclusive" /> + <device match="rawip6" ip-type="exclusive" /> + <device match="rts" ip-type="exclusive" /> + <device match="sad/admin" ip-type="exclusive" /> + <device match="sctp" ip-type="exclusive" /> + <device match="sctp6" ip-type="exclusive" /> + <device match="spdsock" ip-type="exclusive" /> + <device match="sppp" ip-type="exclusive" /> + <device match="sppptun" ip-type="exclusive" /> + <device match="vni" ip-type="exclusive" /> + + <!-- Renamed devices to create under /dev --> + <device match="zcons/%z/zoneconsole" name="zconsole" /> + + <!-- Symlinks to create under /dev --> + <symlink source="console" target="zconsole" /> + <symlink source="dtremote" target="/dev/null" /> + <symlink source="msglog" target="zconsole" /> + <symlink source="stderr" target="./fd/2" /> + <symlink source="stdin" target="./fd/0" /> + <symlink source="stdout" target="./fd/1" /> + <symlink source="syscon" target="zconsole" /> + <symlink source="sysmsg" target="zconsole" /> + <symlink source="systty" target="zconsole" /> + +</platform> diff --git a/usr/src/lib/brand/joyent/zone/poststate b/usr/src/lib/brand/joyent/zone/poststate new file mode 100755 index 0000000000..6cd0d0a18f --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/poststate @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="joyent" +. /usr/lib/brand/jcommon/poststate diff --git a/usr/src/lib/brand/joyent/zone/prestate b/usr/src/lib/brand/joyent/zone/prestate new file mode 100755 index 0000000000..10b73285c6 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/prestate @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="joyent" +. /usr/lib/brand/jcommon/prestate diff --git a/usr/src/lib/brand/joyent/zone/statechange b/usr/src/lib/brand/joyent/zone/statechange new file mode 100755 index 0000000000..5e2eeff0f2 --- /dev/null +++ b/usr/src/lib/brand/joyent/zone/statechange @@ -0,0 +1,42 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018 Joyent, Inc. All rights reserved. +# + +# Do we support vrrp? +jst_vrrp="yes" + +# Do we support unfiltered promiscuous mode +# jst_ufpromisc="yes" + +# Do we use vnd devices +# jst_createvnd="yes" + +# Do we not need to have various SMF and ccs stuff created for us? +# jst_simplefs="yes" + +# Should we show snapshots? +jst_showsnap="yes" + +# Where does the mdata socket live? +jst_mdatapath="/.zonecontrol/" + +# Ensure docker zfd(7D) log is not put in the wrong place +if [[ "$_ZONECFG_attr_docker" == true ]]; then + typeset -A FORCED_ATTRS + FORCED_ATTRS["zlog-name"]= +fi + +. /usr/lib/brand/jcommon/statechange diff --git a/usr/src/lib/brand/kvm/Makefile b/usr/src/lib/brand/kvm/Makefile new file mode 100644 index 0000000000..0823a1f94e --- /dev/null +++ b/usr/src/lib/brand/kvm/Makefile @@ -0,0 +1,31 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2019 Joyent, Inc. +# + +default: all + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS = zone + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber + +all install clean clobber: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/kvm/zone/Makefile b/usr/src/lib/brand/kvm/zone/Makefile new file mode 100644 index 0000000000..0fc0a1a375 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/Makefile @@ -0,0 +1,35 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2019 Joyent, Inc. +# + +BRAND = kvm + +include $(SRC)/lib/brand/Makefile.brand + +PROGS = kattach kdetach kinstall kuninstall prestate poststate statechange +XMLDOCS = config.xml platform.xml + +CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTPROGS) + +# These shouldn't be necessary, but for some reason the same thing in +# ../../Makefile.brand is not being picked up. +$(ROOTPROGS) := FILEMODE = 755 +$(ROOTXMLDOCS) := FILEMODE = 444 + +all: $(PROGS) + +install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) + +clean: + $(RM) $(CLEANFILES) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/kvm/zone/config.xml b/usr/src/lib/brand/kvm/zone/config.xml new file mode 100644 index 0000000000..461805ae1f --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/config.xml @@ -0,0 +1,77 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2010,2011 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN" + "file:///usr/share/lib/xml/dtd/brand.dtd.1"> + +<brand name="kvm"> + <modname></modname> + + <initname>/startvm</initname> + <restartinit>false</restartinit> + <login_cmd>/usr/bin/login -z %Z %u</login_cmd> + <forcedlogin_cmd>/usr/bin/login -z %Z -f %u</forcedlogin_cmd> + <user_cmd>/usr/bin/getent passwd %u</user_cmd> + + <install>/usr/lib/brand/kvm/kinstall -z %z -R %R</install> + <installopts>R:t:U:q:z:</installopts> + <boot></boot> + <halt></halt> + <verify_cfg></verify_cfg> + <verify_adm></verify_adm> + <postclone></postclone> + <postinstall></postinstall> + <attach>/usr/lib/brand/kvm/kattach -z %z -R %R</attach> + <detach>/usr/lib/brand/kvm/kdetach -z %z -R %R</detach> + <clone></clone> + <uninstall>/usr/lib/brand/kvm/kuninstall -z %z -R %R</uninstall> + <prestatechange>/usr/lib/brand/kvm/prestate %z %R</prestatechange> + <poststatechange>/usr/lib/brand/kvm/poststate %z %R</poststatechange> + + <privilege set="default" name="net_rawaccess" ip-type="exclusive" /> + <privilege set="default" name="proc_clock_highres" /> + <privilege set="default" name="proc_lock_memory" /> + <privilege set="default" name="sys_admin" /> + <privilege set="default" name="sys_mount" /> + + <privilege set="prohibited" name="dtrace_kernel" /> + <privilege set="prohibited" name="proc_zone" /> + <privilege set="prohibited" name="sys_config" /> + <privilege set="prohibited" name="sys_devices" /> + <privilege set="prohibited" name="sys_ip_config" ip-type="shared" /> + <privilege set="prohibited" name="sys_linkdir" /> + <privilege set="prohibited" name="sys_net_config" /> + <privilege set="prohibited" name="sys_res_config" /> + <privilege set="prohibited" name="sys_suser_compat" /> + <privilege set="prohibited" name="xvm_control" /> + <privilege set="prohibited" name="virt_manage" /> + <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" /> + + <privilege set="required" name="proc_exec" /> + <privilege set="required" name="sys_mount" /> +</brand> diff --git a/usr/src/lib/brand/kvm/zone/kattach b/usr/src/lib/brand/kvm/zone/kattach new file mode 100755 index 0000000000..17d1242c83 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/kattach @@ -0,0 +1,26 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_kvm_final_setup +} + +ps_brand="kvm" +. /usr/lib/brand/jcommon/cattach diff --git a/usr/src/lib/brand/kvm/zone/kdetach b/usr/src/lib/brand/kvm/zone/kdetach new file mode 100755 index 0000000000..f3785b45e2 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/kdetach @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="kvm" +. /usr/lib/brand/jcommon/cdetach diff --git a/usr/src/lib/brand/kvm/zone/kinstall b/usr/src/lib/brand/kvm/zone/kinstall new file mode 100755 index 0000000000..b070f3a176 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/kinstall @@ -0,0 +1,31 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# Does this brand support reprovisioning? +# jst_reprovision="yes" + +# Is a template image optional? +jst_tmplopt="yes" + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + jattach_kvm_final_setup +} + +. /usr/lib/brand/jcommon/cinstall diff --git a/usr/src/lib/brand/kvm/zone/kuninstall b/usr/src/lib/brand/kvm/zone/kuninstall new file mode 100755 index 0000000000..136044ad9e --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/kuninstall @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +function jcommon_uninstall_hook +{ + : +} + +. /usr/lib/brand/jcommon/cuninstall diff --git a/usr/src/lib/brand/kvm/zone/platform.xml b/usr/src/lib/brand/kvm/zone/platform.xml new file mode 100644 index 0000000000..825344b1e8 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/platform.xml @@ -0,0 +1,145 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2010, 2011 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN" + "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1"> + +<platform name="kvm" allow-exclusive-ip="true"> + + <!-- Global filesystems to mount when booting the zone --> + <global_mount special="/dev" directory="/dev" type="dev" + opt="attrdir=%R/root/dev"/> + + <global_mount special="/lib" directory="/lib" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/sbin" directory="/sbin" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/usr" directory="/usr" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/smartdc" directory="/smartdc" + opt="ro,nodevices" type="lofs" /> + + <!-- Local filesystems to mount when booting the zone --> + <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" /> + + <!-- Devices to create under /dev --> + <device match="arp" /> + <device match="bpf" /> + <device match="conslog" /> + <device match="cpu/self/cpuid" /> + <device match="crypto" /> + <device match="cryptoadm" /> + <device match="dsk" /> + <device match="dtrace/*" /> + <device match="dtrace/provider/*" /> + <device match="fd" /> + <device match="ipnet" /> + <device match="kstat" /> + <device match="kvm" /> + <device match="lo0" /> + <device match="lofictl" /> + <device match="lofi" /> + <device match="log" /> + <device match="logindmux" /> + <device match="nsmb" /> + <device match="net/*" /> + <device match="null" /> + <device match="openprom" arch="sparc" /> + <device match="poll" /> + <device match="pool" /> + <device match="ptmx" /> + <device match="pts/*" /> + <device match="random" /> + <device match="rdsk" /> + <device match="rlofi" /> + <device match="rmt" /> + <device match="sad/user" /> + <device match="svvslo0" /> + <device match="svvslo1" /> + <device match="svvslo2" /> + <device match="svvslo3" /> + <device match="swap" /> + <device match="sysevent" /> + <device match="tcp" /> + <device match="tcp6" /> + <device match="term" /> + <device match="ticlts" /> + <device match="ticots" /> + <device match="ticotsord" /> + <device match="tty" /> + <device match="udp" /> + <device match="udp6" /> + <device match="urandom" /> + <device match="vnd/*" /> + <device match="zero" /> + <device match="zfs" /> + + <!-- Devices to create in exclusive IP zone only --> + <device match="dld" ip-type="exclusive" /> + <device match="icmp" ip-type="exclusive" /> + <device match="icmp6" ip-type="exclusive" /> + <device match="ip" ip-type="exclusive" /> + <device match="ip6" ip-type="exclusive" /> + <device match="ipauth" ip-type="exclusive" /> + <device match="ipf" ip-type="exclusive" /> + <device match="ipl" ip-type="exclusive" /> + <device match="iplookup" ip-type="exclusive" /> + <device match="ipmpstub" ip-type="exclusive" /> + <device match="ipnat" ip-type="exclusive" /> + <device match="ipscan" ip-type="exclusive" /> + <device match="ipsecah" ip-type="exclusive" /> + <device match="ipsecesp" ip-type="exclusive" /> + <device match="ipstate" ip-type="exclusive" /> + <device match="ipsync" ip-type="exclusive" /> + <device match="keysock" ip-type="exclusive" /> + <device match="rawip" ip-type="exclusive" /> + <device match="rawip6" ip-type="exclusive" /> + <device match="rts" ip-type="exclusive" /> + <device match="sad/admin" ip-type="exclusive" /> + <device match="sctp" ip-type="exclusive" /> + <device match="sctp6" ip-type="exclusive" /> + <device match="spdsock" ip-type="exclusive" /> + <device match="sppp" ip-type="exclusive" /> + <device match="sppptun" ip-type="exclusive" /> + <device match="vni" ip-type="exclusive" /> + + <!-- Renamed devices to create under /dev --> + <device match="zcons/%z/zoneconsole" name="zconsole" /> + + <!-- Symlinks to create under /dev --> + <symlink source="console" target="zconsole" /> + <symlink source="dtremote" target="/dev/null" /> + <symlink source="msglog" target="zconsole" /> + <symlink source="stderr" target="./fd/2" /> + <symlink source="stdin" target="./fd/0" /> + <symlink source="stdout" target="./fd/1" /> + <symlink source="syscon" target="zconsole" /> + <symlink source="sysmsg" target="zconsole" /> + <symlink source="systty" target="zconsole" /> + +</platform> diff --git a/usr/src/lib/brand/kvm/zone/poststate b/usr/src/lib/brand/kvm/zone/poststate new file mode 100755 index 0000000000..17060a3b68 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/poststate @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="kvm" +. /usr/lib/brand/jcommon/poststate diff --git a/usr/src/lib/brand/kvm/zone/prestate b/usr/src/lib/brand/kvm/zone/prestate new file mode 100755 index 0000000000..a05fe69368 --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/prestate @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="kvm" +. /usr/lib/brand/jcommon/prestate diff --git a/usr/src/lib/brand/kvm/zone/statechange b/usr/src/lib/brand/kvm/zone/statechange new file mode 100755 index 0000000000..4e3fab34ca --- /dev/null +++ b/usr/src/lib/brand/kvm/zone/statechange @@ -0,0 +1,36 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# Do we support vrrp? +# jst_vrrp="yes" + +# Do we support unfiltered promiscuous mode +jst_ufpromisc="yes" + +# Do we use vnd devices +jst_createvnd="yes" + +# Do we not need to have various SMF and ccs stuff created for us? +jst_simplefs="yes" + +# Should we show snapshots? +# jst_showsnap="yes" + +# Where does the mdata socket live? +jst_mdatapath="/var/run/smartdc" + +. /usr/lib/brand/jcommon/statechange diff --git a/usr/src/lib/brand/lx/Makefile b/usr/src/lib/brand/lx/Makefile new file mode 100644 index 0000000000..bdf0c9f441 --- /dev/null +++ b/usr/src/lib/brand/lx/Makefile @@ -0,0 +1,55 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2017 Joyent, Inc. +# + +default: all + +include Makefile.lx + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS= librtld_db lx_support lx_init lx_lockd lx_brand netfiles \ + zone lx_vdso testing .WAIT +MSGSUBDIRS= lx_brand lx_support zone + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint +_msg := TARGET= _msg + +.KEEP_STATE: + +all install clean clobber lint: $(SUBDIRS) + +_msg: $(MSGSUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/lx/Makefile.lx b/usr/src/lib/brand/lx/Makefile.lx new file mode 100644 index 0000000000..4db4679cef --- /dev/null +++ b/usr/src/lib/brand/lx/Makefile.lx @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/brand/lx/Makefile.lx +# +# include global definitions + +BRAND= lx + +include $(SRC)/lib/brand/Makefile.brand + diff --git a/usr/src/lib/brand/lx/librtld_db/Makefile b/usr/src/lib/brand/lx/librtld_db/Makefile new file mode 100644 index 0000000000..2fc0a818f6 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/Makefile @@ -0,0 +1,54 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +default: all + +include $(SRC)/lib/Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +LINT_SUBDIRS= $(MACH) +$(BUILD64)LINT_SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all install clean clobber: $(SUBDIRS) + +lint: $(LINT_SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/lx/librtld_db/Makefile.com b/usr/src/lib/brand/lx/librtld_db/Makefile.com new file mode 100644 index 0000000000..0237de1cc3 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/Makefile.com @@ -0,0 +1,84 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2020 Joyent, Inc. + +LIBRARY = lx_librtld_db.a +VERS = .1 +COBJS = lx_librtld_db.o +OBJECTS = $(COBJS) $(COBJS64) + +include $(SRC)/lib/Makefile.lib +include ../../Makefile.lx + +CSRCS = $(COBJS:%o=../common/%c) +SRCS = $(CSRCS) + +SRCDIR = ../common +UTSBASE = $(SRC)/uts + +# +# ATTENTION: +# Librtl_db brand plugin libraries should NOT directly invoke any +# libproc.so interfaces or be linked against libproc. If a librtl_db +# brand plugin library uses libproc.so interfaces then it may break +# any other librtld_db consumers (like mdb) that tries to attach +# to a branded process. The only safe interfaces that the a librtld_db +# brand plugin library can use to access a target process are the +# proc_service(3PROC) apis. +# +MAPFILES = $(BRAND_SHARED)/librtld_db/common/mapfile-vers +DYNFLAGS += $(VERSREF) +LIBS = $(DYNLIB) +LDLIBS += -lc -lrtld_db +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I../ -I$(UTSBASE)/common/brand/lx \ + -I$(SRC)/cmd/sgs/librtld_db/common \ + -I$(SRC)/cmd/sgs/include \ + -I$(SRC)/cmd/sgs/include/$(MACH) + +# not linted +SMATCH=off + +ROOTLIBDIR = $(ROOT)/usr/lib/brand/lx +ROOTLIBDIR64 = $(ROOT)/usr/lib/brand/lx/$(MACH64) + +# +# The top level Makefiles define TEXT_DOMAIN. But librtld_db.so.1 isn't +# internationalized and this library won't be either. The only messages that +# this library can generate are messages used for debugging the operation of the +# library itself. +# +DTEXTDOM = + +.KEEP_STATE: + +all: $(LIBS) + +pics/%64.o: ../common/%.c + $(COMPILE.c) -D_ELF64 $(PICFLAGS) -o $@ $< + $(POST_PROCESS_O) + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/brand/lx/librtld_db/amd64/Makefile b/usr/src/lib/brand/lx/librtld_db/amd64/Makefile new file mode 100644 index 0000000000..bdef5f5adb --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/amd64/Makefile @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2020 Joyent, Inc. +# + +COBJS64 = lx_librtld_db64.o + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB) + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c b/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c new file mode 100644 index 0000000000..9f729ab1a6 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c @@ -0,0 +1,853 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/link.h> +#include <libproc.h> +#include <proc_service.h> +#include <rtld_db.h> +#include <synch.h> + +#include <sys/lx_brand.h> + +/* + * Overview of this library derived from the original "BrandZ" PSARC design + * document. + * + * Since Linux binaries are standard ELF objects, Illumos debug tools (i.e. mdb + * or ptools) are able to process them in essentially the same way that Illumos + * binaries are processed. The main objective is to retrieve symbols and + * thereby aid debugging and observability. Unfortunately, most Linux + * distributions strip(1) their binaries as a misguided "optimization" so the + * majority of the useful debugging information is lost. + * + * The debug tools use interfaces provided by librtld_db to debug live + * processes and core files. librtld_db discovers ELF objects which have been + * mapped into the target's address space and reports these back to the tool. + * The librtld_db library understands enough of the internals of the Illumos + * runtime linker to iterate over the linker's private link maps and process + * the objects it finds. librtld_db allows our tools to debug the Illumos + * portions of a branded process (e.g. the brand library, libc, etc.) but they + * can't understand any Linux objects that are mapped into the address space + * because the Illumos linker only has Illumos objects on its link maps. + * + * In order to give the tools visibility into Linux binaries, a brand helper + * framework is implemented in librtld_db. When librtld_db is asked to examine + * a branded target process or core file, it uses the AT_SUN_BRANDNAME aux + * vector to get our brand name (lx). It then dlopen-s this lx_librtld_db.so + * helper library. + * + * Once loaded, this helper library is responsible for finding any lx-specific + * information it needs, such as the Linux equivalent LDDATA aux entry and + * preparing to return details about the objects loaded into the address space + * by the Linux linker. + * + * When a debug tool asks to know what objects are loaded in the target, + * librtld_db walks the Illumos link maps and iterates over each object it + * finds there, handing information about each to the tool. It then calls down + * into this helper library, which does the same for the brand-specific objects + * used by the target. + * + * This debug-helper code contains a bunch of helpful ps_plog calls. To enable + * this output with the ptools (e.g. pmap) set LIBPROC_DEBUG=1 in your + * environment. To enable it with mdb set MDB_DEBUG=psvc in your environment. + */ + +/* + * ATTENTION: + * Librtl_db brand plugin libraries should NOT directly invoke any + * libproc.so interfaces or be linked against libproc. If a librtl_db + * brand plugin library uses libproc.so interfaces then it may break + * any other librtld_db consumers (like mdb) that tries to attach + * to a branded process. The only safe interfaces that the a librtld_db + * brand plugin library can use to access a target process are the + * proc_service(3PROC) apis. + */ + +/* + * M_DATA comes from some streams header file but is also redifined in + * _rtld_db.h, so nuke the old streams definition here. + */ +#ifdef M_DATA +#undef M_DATA +#endif /* M_DATA */ + +/* + * For 32-bit versions of this library, this file gets compiled once. + * For 64-bit versions of this library, this file gets compiled twice, + * once with _ELF64 defined and once without. The expectation is that + * the 64-bit version of the library can properly deal with both 32-bit + * and 64-bit elf files, hence in the 64-bit library there are two copies + * of all the interfaces in this file, one set named *32 and one named *64. + * + * This also means that we need to be careful when declaring local pointers + * that point to objects in another processes address space, since these + * pointers may not match the current processes pointer width. Basically, + * we should avoid using data types that change size between 32 and 64 bit + * modes like: long, void *, uintptr_t, caddr_t, psaddr_t, size_t, etc. + * Instead we should declare all pointers as uint32_t. Then when we + * are compiled to deal with 64-bit targets we'll re-define uint32_t + * to be a uint64_t. + */ +#ifdef _LP64 +#ifdef _ELF64 +#define lx_ldb_get_dyns32 lx_ldb_get_dyns64 +#define lx_ldb_init32 lx_ldb_init64 +#define lx_ldb_fini32 lx_ldb_fini64 +#define lx_ldb_loadobj_iter32 lx_ldb_loadobj_iter64 +#define lx_ldb_getauxval32 lx_ldb_getauxval64 +#define lx_elf_props32 lx_elf_props64 +#define _rd_get_dyns32 _rd_get_dyns64 +#define _rd_get_ehdr32 _rd_get_ehdr64 +#define uint32_t uint64_t +#define Elf32_Dyn Elf64_Dyn +#define Elf32_Ehdr Elf64_Ehdr +#define Elf32_Phdr Elf64_Phdr +#define Elf32_Sym Elf64_Sym +#endif /* _ELF64 */ +#endif /* _LP64 */ + +/* Included from usr/src/cmd/sgs/librtld_db/common */ +#include <_rtld_db.h> + +typedef struct lx_rd { + rd_agent_t *lr_rap; + struct ps_prochandle *lr_php; /* proc handle pointer */ + uint32_t lr_rdebug; /* address of lx r_debug */ + uint32_t lr_exec; /* base address of executable */ +} lx_rd_t; + +typedef struct lx_link_map { + uint32_t lxm_addr; /* Base address shared object is loaded at. */ + uint32_t lxm_name; /* Absolute file name object was found in. */ + uint32_t lxm_ld; /* Dynamic section of the shared object. */ + uint32_t lxm_next; /* Chain of loaded objects. */ +} lx_link_map_t; + +typedef struct lx_r_debug { + int r_version; /* Version number for this protocol. */ + uint32_t r_map; /* Head of the chain of loaded objects. */ + + /* + * This is the address of a function internal to the run-time linker, + * that will always be called when the linker begins to map in a + * library or unmap it, and again when the mapping change is complete. + * The debugger can set a breakpoint at this address if it wants to + * notice shared object mapping changes. + */ + uint32_t r_brk; + r_state_e r_state; /* defined the same way between lx/solaris */ + uint32_t r_ldbase; /* Base address the linker is loaded at. */ +} lx_r_debug_t; + +static uint32_t +lx_ldb_getauxval32(struct ps_prochandle *php, int type) +{ + const auxv_t *auxvp = NULL; + + if (ps_pauxv(php, &auxvp) != PS_OK) + return ((uint32_t)-1); + + while (auxvp->a_type != AT_NULL) { + if (auxvp->a_type == type) + return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr); + auxvp++; + } + return ((uint32_t)-1); +} + +/* + * A key difference between the linux linker and ours' is that the linux + * linker adds the base address of segments to certain values in the + * segments' ELF header. As an example, look at the address of the + * DT_HASH hash table in a Solaris section - it is a relative address + * which locates the start of the hash table, relative to the beginning + * of the ELF file. However, when the linux linker loads a section, it + * modifies the in-memory ELF image by changing address of the hash + * table to be an absolute address. This is only done for libraries - not for + * executables. + * + * Solaris tools expect the relative address to remain relative, so + * here we will modify the in-memory ELF image so that it once again + * contains relative addresses. + * + * To accomplish this, we walk through all sections in the target. + * Linux sections are identified by pointing to the linux linker or libc in the + * DT_NEEDED section. For all matching sections, we subtract the segment + * base address to get back to relative addresses. + */ +static rd_err_e +lx_ldb_get_dyns32(rd_helper_data_t rhd, + psaddr_t addr, void **dynpp, size_t *dynpp_sz) +{ + lx_rd_t *lx_rd = (lx_rd_t *)rhd; + rd_agent_t *rap = lx_rd->lr_rap; + Elf32_Ehdr ehdr; + Elf32_Dyn *dynp = NULL; + size_t dynp_sz; + uint_t ndyns; + int i; + + ps_plog("lx_ldb_get_dyns: invoked for object at 0x%p", addr); + + /* Read in a copy of the ehdr */ + if (_rd_get_ehdr32(rap, addr, &ehdr, NULL) != RD_OK) { + ps_plog("lx_ldb_get_dyns: _rd_get_ehdr() failed"); + return (RD_ERR); + } + + /* read out the PT_DYNAMIC elements for this object */ + if (_rd_get_dyns32(rap, addr, &dynp, &dynp_sz) != RD_OK) { + ps_plog("lx_ldb_get_dyns: _rd_get_dyns() failed"); + return (RD_ERR); + } + + /* + * From here on out if we encounter an error we'll just return + * success and pass back the unmolested dynamic elements that + * we've already obtained. + */ + if (dynpp != NULL) + *dynpp = dynp; + if (dynpp_sz != NULL) + *dynpp_sz = dynp_sz; + ndyns = dynp_sz / sizeof (Elf32_Dyn); + + /* If this isn't a dynamic object, there's nothing left todo */ + if (ehdr.e_type != ET_DYN) { + ps_plog("lx_ldb_get_dyns: done: not a shared object"); + return (RD_OK); + } + + /* + * Before we blindly start changing dynamic section addresses + * we need to figure out if the current object that we're looking + * at is a linux object or a solaris object. To do this first + * we need to find the string tab dynamic section element. + */ + for (i = 0; i < ndyns; i++) { + if (dynp[i].d_tag == DT_STRTAB) + break; + } + if (i == ndyns) { + ps_plog("lx_ldb_get_dyns: " + "failed to find string tab in the dynamic section"); + return (RD_OK); + } + + /* + * Check if the strtab value looks like an offset or an address. + * It's an offset if the value is less then the base address that + * the object is loaded at, or if the value is less than the offset + * of the section headers in the same elf object. This check isn't + * perfect, but in practice it's good enough. + */ + if ((dynp[i].d_un.d_ptr < addr) || + (dynp[i].d_un.d_ptr < ehdr.e_shoff)) { + ps_plog("lx_ldb_get_dyns: " + "doesn't appear to be an lx object"); + return (RD_OK); + } + + /* + * This seems to be a a linux object, so we'll patch up the dynamic + * section addresses + */ + ps_plog("lx_ldb_get_dyns: " + "patching up lx object dynamic section addresses"); + for (i = 0; i < ndyns; i++) { + switch (dynp[i].d_tag) { + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_REL: + case DT_DEBUG: + case DT_JMPREL: + case DT_VERSYM: + if (dynp[i].d_un.d_val > addr) { + dynp[i].d_un.d_ptr -= addr; + } + break; + default: + break; + } + } + return (RD_OK); +} + +static void +lx_ldb_fini32(rd_helper_data_t rhd) +{ + lx_rd_t *lx_rd = (lx_rd_t *)rhd; + ps_plog("lx_ldb_fini: cleaning up lx helper"); + free(lx_rd); +} + +/* + * The linux linker has an r_debug structure somewhere in its data section that + * contains the address of the head of the link map list. To find this, we will + * use the DT_DEBUG token in the executable's dynamic section. The linux linker + * wrote the address of its r_debug structure to the DT_DEBUG dynamic entry. We + * get the address of the executable's program headers from the + * AT_SUN_BRAND_LX_PHDR aux vector entry. From there, we calculate the + * address of the Elf header, and from there we can easily get to the DT_DEBUG + * entry. + */ +static rd_helper_data_t +lx_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php) +{ + lx_rd_t *lx_rd; + uint32_t addr, phdr_addr, dyn_addr; + uint32_t symtab, strtab, offs; + uint32_t vaddr, memsz; + caddr_t mem; + Elf32_Dyn *dyn; + Elf32_Phdr phdr, *ph, *dph, *phdrs; + Elf32_Ehdr ehdr; + Elf32_Sym *sym; + int i, dyn_count; + + lx_rd = calloc(sizeof (lx_rd_t), 1); + if (lx_rd == NULL) { + ps_plog("lx_ldb_init: cannot allocate memory"); + return (NULL); + } + lx_rd->lr_rap = rap; + lx_rd->lr_php = php; + + phdr_addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_PHDR); + if (phdr_addr == (uint32_t)-1) { + ps_plog("lx_ldb_init: no LX_PHDR found in aux vector"); + return (NULL); + } + ps_plog("lx_ldb_init: found LX_PHDR auxv phdr at: 0x%p", + phdr_addr); + + if (ps_pread(php, phdr_addr, &phdr, sizeof (phdr)) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read phdr at 0x%p", + phdr_addr); + free(lx_rd); + return (NULL); + } + + /* The ELF header should be before the program header in memory */ + lx_rd->lr_exec = addr = phdr_addr - phdr.p_offset; + if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read ehdr at 0x%p", + lx_rd->lr_exec); + free(lx_rd); + return (NULL); + } + ps_plog("lx_ldb_init: read ehdr at: 0x%p", addr); + ps_plog("lx_ldb_init: ehdr t %d ent 0x%p poff 0x%p psize %d pnum %d", + ehdr.e_type, ehdr.e_entry, ehdr.e_phoff, ehdr.e_phentsize, + ehdr.e_phnum); + + if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) { + ps_plog("lx_ldb_init: couldn't alloc phdrs memory"); + free(lx_rd); + return (NULL); + } + + if (ps_pread(php, phdr_addr, phdrs, ehdr.e_phnum * ehdr.e_phentsize) != + PS_OK) { + ps_plog("lx_ldb_init: couldn't read phdrs at 0x%p", + phdr_addr); + free(lx_rd); + free(phdrs); + return (NULL); + } + + /* program headers */ + ps_plog("lx_ldb_init: read %d phdrs at: 0x%p", + ehdr.e_phnum, phdr_addr); + + for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++, + /*LINTED */ + ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) { + ps_plog("lx_ldb_init: ph[%d] 0x%p type %d", i, + (phdr_addr + ((char *)ph - (char *)phdrs)), ph->p_type); + if (ph->p_type == PT_DYNAMIC) + break; + } + if (i == ehdr.e_phnum) { + ps_plog("lx_ldb_init: no PT_DYNAMIC in executable"); + free(lx_rd); + free(phdrs); + return (NULL); + } + ps_plog("lx_ldb_init: found PT_DYNAMIC phdr[%d] at: 0x%p", + i, (phdr_addr + ((char *)ph - (char *)phdrs))); + ps_plog("lx_ldb_init: ph t 0x%x f 0x%x o 0x%p v 0x%p s %d", + ph->p_type, ph->p_flags, ph->p_offset, ph->p_vaddr, ph->p_filesz); + + if ((dyn = malloc(ph->p_filesz)) == NULL) { + ps_plog("lx_ldb_init: couldn't alloc for PT_DYNAMIC"); + free(lx_rd); + free(phdrs); + return (NULL); + } + + /* + * Unclear why the dyn_addr works sometimes with one value and + * sometimes for the other, so we handle both cases. + */ + + dyn_count = ph->p_filesz / sizeof (Elf32_Dyn); + ps_plog("lx_ldb_init: dyn_count %d %d", dyn_count, sizeof (Elf32_Dyn)); + dyn_addr = addr + ph->p_vaddr; + ps_plog("lx_ldb_init: dyn_addr 0x%p 0x%x = 0x%p", + addr, ph->p_offset, dyn_addr); + if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p, " + "trying dyn_addr 0x%p", + dyn_addr, ph->p_vaddr); + + dyn_addr = ph->p_vaddr; + if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p", + dyn_addr); + + free(lx_rd); + free(phdrs); + free(dyn); + return (NULL); + } + } + ps_plog("lx_ldb_init: read %d dynamic headers at: 0x%p", + dyn_count, dyn_addr); + + for (i = 0; i < dyn_count; i++) { + if (dyn[i].d_tag == DT_DEBUG) { + lx_rd->lr_rdebug = dyn[i].d_un.d_ptr; + break; + } + } + free(phdrs); + free(dyn); + + if (lx_rd->lr_rdebug != 0) { + ps_plog("lx_ldb_init: found DT_DEBUG: 0x%p", lx_rd->lr_rdebug); + return ((rd_helper_data_t)lx_rd); + } + + ps_plog("lx_ldb_init: no DT_DEBUG found in exe; looking for r_debug"); + + /* + * If we didn't find DT_DEBUG, we're going to employ the same fallback + * as gdb: pawing through the dynamic linker's symbol table looking + * for the r_debug symbol. + */ + addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_INTERP); + + if (addr == (uint32_t)-1) { + ps_plog("lx_ldb_init: no interpreter; failing"); + free(lx_rd); + return (NULL); + } + + ps_plog("lx_ldb_init: reading interp ehdr at 0x%p", addr); + + if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read interp ehdr at 0x%p", addr); + free(lx_rd); + return (NULL); + } + + if (ehdr.e_type != ET_DYN) { + ps_plog("lx_ldb_init: interp ehdr not of type ET_DYN"); + free(lx_rd); + return (NULL); + } + + phdr_addr = addr + ehdr.e_phoff; + + if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) { + ps_plog("lx_ldb_init: couldn't alloc interp phdrs memory"); + free(lx_rd); + return (NULL); + } + + if (ps_pread(php, phdr_addr, phdrs, + ehdr.e_phnum * ehdr.e_phentsize) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read interp phdrs at 0x%p", + phdr_addr); + free(lx_rd); + free(phdrs); + return (NULL); + } + + ps_plog("lx_ldb_init: read %d interp phdrs at: 0x%p", + ehdr.e_phnum, phdr_addr); + + vaddr = (uint32_t)-1; + memsz = 0; + + for (i = 0, ph = phdrs, dph = NULL; i < ehdr.e_phnum; i++, + /*LINTED */ + ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) { + /* + * Keep track of our lowest PT_LOAD address, as this segment + * contains the DT_SYMTAB and DT_STRTAB. + */ + if (ph->p_type == PT_LOAD && ph->p_vaddr < vaddr) { + vaddr = ph->p_vaddr; + memsz = ph->p_memsz; + } + + if (ph->p_type == PT_DYNAMIC) + dph = ph; + } + + if (vaddr == (uint32_t)-1 || memsz == 0) { + ps_plog("lx_ldb_init: no PT_LOAD section in interp"); + free(lx_rd); + free(phdrs); + return (NULL); + } + + ps_plog("lx_ldb_init: found interp PT_LOAD to be %d bytes at 0x%p", + memsz, vaddr); + + if ((ph = dph) == NULL) { + ps_plog("lx_ldb_init: no PT_DYNAMIC in interp"); + free(lx_rd); + free(phdrs); + return (NULL); + } + + ps_plog("lx_ldb_init: found interp PT_DYNAMIC phdr[%d] at: 0x%p", + i, (phdr_addr + ((char *)ph - (char *)phdrs))); + + if ((dyn = malloc(ph->p_filesz)) == NULL) { + ps_plog("lx_ldb_init: couldn't alloc for interp PT_DYNAMIC"); + free(lx_rd); + free(phdrs); + return (NULL); + } + + dyn_addr = addr + ph->p_offset; + dyn_count = ph->p_filesz / sizeof (Elf32_Dyn); + + if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read interp dynamic at 0x%p", + dyn_addr); + free(lx_rd); + free(phdrs); + free(dyn); + return (NULL); + } + + free(phdrs); + + ps_plog("lx_ldb_init: read %d interp dynamic headers at: 0x%p", + dyn_count, dyn_addr); + + /* + * As noted in lx_ldb_get_dyns32(), in Linux, the PT_DYNAMIC table + * is adjusted to represent absolute addresses instead of offsets. + * This is not true for the interpreter, however -- where the values + * will be represented as offsets from the lowest PT_LOAD p_vaddr. + */ + symtab = strtab = (uint32_t)-1; + + for (i = 0; i < dyn_count; i++) { + if (dyn[i].d_tag == DT_STRTAB) + strtab = dyn[i].d_un.d_ptr - vaddr; + + if (dyn[i].d_tag == DT_SYMTAB) + symtab = dyn[i].d_un.d_ptr - vaddr; + } + + free(dyn); + + if (strtab == (uint32_t)-1 || strtab > memsz) { + ps_plog("lx_ldb_init: didn't find valid interp strtab"); + free(lx_rd); + return (NULL); + } + + if (symtab == (uint32_t)-1 || symtab > memsz) { + ps_plog("lx_ldb_init: didn't find valid interp symtab"); + free(lx_rd); + return (NULL); + } + + ps_plog("lx_ldb_init: strtab is 0x%p, symtab is 0x%p", + addr + strtab, addr + symtab); + + if ((mem = malloc(memsz)) == NULL) { + ps_plog("lx_ldb_init: couldn't allocate interp " + "buffer of 0x%p bytes", memsz); + free(lx_rd); + return (NULL); + } + + if (ps_pread(php, addr, mem, memsz) != PS_OK) { + ps_plog("lx_ldb_init: couldn't read interp at 0x%p", addr); + free(lx_rd); + free(mem); + return (NULL); + } + + /* + * We make an assumption that is made elsewhere in the Linux linker: + * that the DT_SYMTAB immediately precedes the DT_STRTAB. + */ + for (offs = symtab; offs < strtab; offs += sizeof (Elf32_Sym)) { + uint32_t p; + + sym = (Elf32_Sym *)&mem[offs]; + + if (sym->st_name > memsz) { + ps_plog("lx_ldb_init: invalid st_name at sym 0x%p", + addr + offs); + continue; + } + + p = sym->st_name; + + if (strtab + p > memsz) { + ps_plog("lx_ldb_init: invalid symbol address 0x%p, " + "memsz 0x%p", strtab + p, memsz); + continue; + } + + if (mem[strtab + p] == '\0') + continue; + + /* Sometimes we're pointing into the middle of a symbol? */ + while ((strtab + p) > 0 && (strtab + p) < memsz && + mem[strtab + p] != '\0') + p--; + p++; + + ps_plog("lx_ldb_init: interp symbol (0x%p) %s", + strtab + p, &mem[strtab + p]); + + if (strcmp(&mem[strtab + p], "_r_debug") == 0) + break; + } + + if (offs >= strtab) { + ps_plog("lx_ldb_init: no _r_debug found in interpreter"); + free(mem); + free(lx_rd); + return (NULL); + } + + lx_rd->lr_rdebug = (sym->st_value - vaddr) + addr; + ps_plog("lx_ldb_init: found _r_debug at 0x%p", lx_rd->lr_rdebug); + free(mem); + + return ((rd_helper_data_t)lx_rd); +} + +/* + * Given the address of an ELF object in the target, return its size and + * the proper link map ID. + */ +static size_t +lx_elf_props32(struct ps_prochandle *php, uint32_t addr, psaddr_t *data_addr) +{ + Elf32_Ehdr ehdr; + Elf32_Phdr *phdrs, *ph; + int i; + uint32_t min = (uint32_t)-1; + uint32_t max = 0; + size_t sz = 0; + + if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) { + ps_plog("lx_elf_props: Couldn't read ELF header at 0x%p", + addr); + return (0); + } + + if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) + return (0); + + if (ps_pread(php, addr + ehdr.e_phoff, phdrs, ehdr.e_phnum * + ehdr.e_phentsize) != PS_OK) { + ps_plog("lx_elf_props: Couldn't read program headers at 0x%p", + addr + ehdr.e_phoff); + return (0); + } + + for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++, + /*LINTED */ + ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) { + + if (ph->p_type != PT_LOAD) + continue; + + if ((ph->p_flags & (PF_W | PF_R)) == (PF_W | PF_R)) { + *data_addr = ph->p_vaddr; + if (ehdr.e_type == ET_DYN) + *data_addr += addr; + if (*data_addr & (ph->p_align - 1)) + *data_addr = *data_addr & (~(ph->p_align -1)); + } + + if (ph->p_vaddr < min) + min = ph->p_vaddr; + + if (ph->p_vaddr > max) { + max = ph->p_vaddr; + sz = ph->p_memsz + max - min; + if (sz & (ph->p_align - 1)) + sz = (sz & (~(ph->p_align - 1))) + ph->p_align; + } + } + + free(phdrs); + return (sz); +} + +static int +lx_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data) +{ + lx_rd_t *lx_rd = (lx_rd_t *)rhd; + struct ps_prochandle *php = lx_rd->lr_php; + lx_r_debug_t r_debug; + lx_link_map_t map; + uint32_t p = (uint32_t)(uintptr_t)NULL; + int rc; + rd_loadobj_t exec; + + if ((rc = ps_pread(php, (psaddr_t)lx_rd->lr_rdebug, &r_debug, + sizeof (r_debug))) != PS_OK) { + ps_plog("lx_ldb_loadobj_iter: " + "Couldn't read linux r_debug at 0x%p", lx_rd->lr_rdebug); + return (rc); + } + + p = r_debug.r_map; + + /* + * The first item on the link map list is for the executable, but it + * doesn't give us any useful information about it. We need to + * synthesize a rd_loadobj_t for the client. + * + * Linux doesn't give us the executable name, so we'll get it from + * the AT_EXECNAME entry instead. + */ + if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != PS_OK) { + ps_plog("lx_ldb_loadobj_iter: " + "Couldn't read linux link map at 0x%p", p); + return (rc); + } + + bzero(&exec, sizeof (exec)); + exec.rl_base = lx_rd->lr_exec; + exec.rl_dynamic = map.lxm_ld; + exec.rl_nameaddr = lx_ldb_getauxval32(php, AT_SUN_EXECNAME); + exec.rl_lmident = LM_ID_BASE; + + exec.rl_bend = exec.rl_base + + lx_elf_props32(php, lx_rd->lr_exec, &exec.rl_data_base); + + if ((*cb)(&exec, client_data) == 0) { + ps_plog("lx_ldb_loadobj_iter: " + "client callb failed for executable"); + return (PS_ERR); + } + ps_plog("lx_ldb_loadobj_iter: exec base 0x%p dyn 0x%p", + exec.rl_base, exec.rl_dynamic); + + for (p = map.lxm_next; p != (uint32_t)(uintptr_t)NULL; + p = map.lxm_next) { + rd_loadobj_t obj; + + if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != + PS_OK) { + ps_plog("lx_ldb_loadobj_iter: " + "Couldn't read lk map at %p", p); + return (rc); + } + + /* + * The linux link map has less information than the Solaris one. + * We need to go fetch the missing information from the ELF + * headers. + */ + + obj.rl_nameaddr = (psaddr_t)map.lxm_name; + obj.rl_base = map.lxm_addr; + obj.rl_refnameaddr = (psaddr_t)map.lxm_name; + obj.rl_plt_base = (psaddr_t)NULL; + obj.rl_plt_size = 0; + obj.rl_lmident = LM_ID_BASE; + + ps_plog("lx_ldb_loadobj_iter: map base 0x%p 0x%p", + obj.rl_base, obj.rl_nameaddr); + + /* + * Ugh - we have to walk the ELF stuff, find the PT_LOAD + * sections, and calculate the end of the file's mappings + * ourselves. + */ + + obj.rl_bend = map.lxm_addr + + lx_elf_props32(php, map.lxm_addr, &obj.rl_data_base); + obj.rl_padstart = obj.rl_base; + obj.rl_padend = obj.rl_bend; + obj.rl_dynamic = map.lxm_ld; + obj.rl_tlsmodid = 0; + + ps_plog("lx_ldb_loadobj_iter: 0x%p to 0x%p", + obj.rl_base, obj.rl_bend); + + if ((*cb)(&obj, client_data) == 0) { + ps_plog("lx_ldb_loadobj_iter: " + "Client callback failed on %s", map.lxm_name); + return (rc); + } + } + return (RD_OK); +} + +/* + * Librtld_db plugin linkage struct. + * + * When we get loaded by librtld_db, it will look for the symbol below + * to find our plugin entry points. + */ +rd_helper_ops_t RTLD_DB_BRAND_OPS = { + LM_ID_BRAND, + lx_ldb_init32, + lx_ldb_fini32, + lx_ldb_loadobj_iter32, + lx_ldb_get_dyns32 +}; diff --git a/usr/src/lib/brand/lx/librtld_db/i386/Makefile b/usr/src/lib/brand/lx/librtld_db/i386/Makefile new file mode 100644 index 0000000000..b5f780c072 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/i386/Makefile @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +CLOBBERFILES = $(ROOTLIBDIR)/$(DYNLIB) + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/brand/lx/lx_brand/Makefile b/usr/src/lib/brand/lx/lx_brand/Makefile new file mode 100644 index 0000000000..fc217b672c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2014 Joyent, Inc. All rights reserved. +# + +include ../../../Makefile.lib + +default: all + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint +_msg := TARGET= _msg + +.KEEP_STATE: + +all install clean clobber lint _msg: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/lx/lx_brand/Makefile.com b/usr/src/lib/brand/lx/lx_brand/Makefile.com new file mode 100644 index 0000000000..f506fc49fc --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/Makefile.com @@ -0,0 +1,103 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2020 Joyent, Inc. +# + +LX_CMN = $(SRC)/common/brand/lx + +LIBRARY = lx_brand.a +VERS = .1 +COBJS = capabilities.o \ + clock.o \ + clone.o \ + debug.o \ + dir.o \ + file.o \ + fork.o \ + lx_brand.o \ + misc.o \ + module.o \ + mount.o \ + mount_nfs.o \ + ptrace.o \ + sendfile.o \ + signal.o \ + stack.o \ + statfs.o \ + sysctl.o \ + sysv_ipc.o \ + time.o \ + truncate.o + +CMNOBJS = lx_auxv.o \ + lx_errno.o \ + lx_signum.o +ASOBJS = lx_handler.o lx_crt.o +OBJECTS = $(CMNOBJS) $(COBJS) $(ASOBJS) + +USDT_PROVIDERS = lx_provider.d + +include ../../Makefile.lx +include ../../../../Makefile.lib + +CSRCS = $(COBJS:%o=../common/%c) $(CMNOBJS:%o=$(LX_CMN)/%c) +ASSRCS = $(ASOBJS:%o=$(ISASRCDIR)/%s) +SRCS = $(CSRCS) $(ASSRCS) + +SRCDIR = ../common +UTSBASE = ../../../../../uts + +LIBS = $(DYNLIB) +LDLIBS += -lmapmalloc -lsocket -lproc -lrtld_db -lrpcsvc -lnsl -lc +DYNFLAGS += $(DYNFLAGS_$(CLASS)) +DYNFLAGS += $(BLOCAL) $(ZNOVERSION) -Wl,-e_start +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I. -I../ -I$(UTSBASE)/common/brand/lx -I$(LX_CMN) +ASFLAGS = -P $(ASFLAGS_$(CURTYPE)) -D_ASM -I../ \ + -I$(UTSBASE)/common/brand/lx + +ZGUIDANCE = -zguidance=nounused + +# not linted +SMATCH=off + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../../Makefile.targ +include ../../../../Makefile.usdt + +pics/%.o: $(ISASRCDIR)/%.s + $(COMPILE.s) -o $@ $< + $(POST_PROCESS_S_O) + +pics/%.o: $(LX_CMN)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/Makefile b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile new file mode 100644 index 0000000000..a1db90cd38 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2015 Joyent, Inc. +# +# +# lib/brand/lx/amd64/Makefile + +ISASRCDIR=. + +ASFLAGS += -P -D_ASM + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +DYNFLAGS += -Wl,-I/native/lib/64/ld.so.1 +CPPFLAGS += -D_SYSCALL32 + +POFILE= lx_brand.po +MSGFILES= $(CSRCS) + +ASSYMDEP_OBJS = lx_handler.o + +install: all $(ROOTLIBS64) + +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) + +_msg: $(MSGDOMAINPOFILE) + +include $(SRC)/Makefile.msg.targ diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s b/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s new file mode 100644 index 0000000000..13a88a3d50 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s @@ -0,0 +1,62 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2014 Joyent, Inc. All rights reserved. + */ + +#include <sys/asm_linkage.h> + +#if defined(lint) + +void +_start(void) +{ +} + +#else /* lint */ + + /* + * C language startup routine for the lx brand shared library. + * + * That routine expects to be called with the following arguments: + * brand_init(int argc, char *argv[], char *envp[]) + * + * There are no arguments explicitly passed to this entry point, + * routine, but we do know how our initial stack has been setup by + * the kernel. The stack format is documented in: + * usr/src/cmd/sgs/rtld/amd64/boot.s + * + * So this routine will troll through the stack to setup the argument + * values for the common brand library startup routine and then invoke + * it. This routine is modeled after the default crt1.s`_start() + * routines. + */ + ENTRY_NP(_start) + pushq $0 / Build a stack frame. retpc = NULL + pushq $0 / fp = NULL + movq %rsp, %rbp / first stack frame + + /* + * Calculate the location of the envp array by adding the size of + * the argv array to the start of the argv array. + */ + movq 16(%rbp), %rdi / argc in %rdi (1st param) + leaq 24(%rbp), %rsi / &argv[0] in %rsi (2nd param) + leaq 32(%rbp,%rdi,8), %rdx / envp in %rdx (3rd param) + call lx_init + + /* lx_init will never return. */ + /*NOTREACHED*/ + SET_SIZE(_start) +#endif /* lint */ diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s b/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s new file mode 100644 index 0000000000..a01d66554d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s @@ -0,0 +1,53 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#include <sys/asm_linkage.h> +#include <sys/regset.h> +#include <sys/segments.h> +#include <sys/syscall.h> +#include <sys/lx_brand.h> + +#if defined(_ASM) +#include <sys/lx_signal.h> +#include <sys/lx_syscall.h> +#endif /* _ASM */ + +/* 64-bit signal syscall numbers */ +#define LX_SYS_rt_sigreturn 15 + +#if defined(lint) + +#include <sys/types.h> +#include <sys/regset.h> +#include <sys/signal.h> + +void +lx_rt_sigreturn_tramp(void) +{} + +#else /* lint */ + + /* + * Trampoline code is called by the return at the end of a Linux + * signal handler to return control to the interrupted application + * via the lx_rt_sigreturn() syscall. + */ + ENTRY_NP(lx_rt_sigreturn_tramp) + movq $LX_SYS_rt_sigreturn, %rax + syscall + SET_SIZE(lx_rt_sigreturn_tramp) + +#endif /* lint */ diff --git a/usr/src/lib/brand/lx/lx_brand/common/capabilities.c b/usr/src/lib/brand/lx/lx_brand/common/capabilities.c new file mode 100644 index 0000000000..e034560452 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/capabilities.c @@ -0,0 +1,516 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * LX Brand emulation of capget/capset syscalls + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/debug.h> +#include <sys/lx_types.h> +#include <sys/lx_syscall.h> +#include <sys/syscall.h> +#include <alloca.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <sys/lx_misc.h> +#include <priv.h> + +typedef struct { + uint32_t version; + int pid; +} lx_cap_user_header_t; + +typedef struct { + uint32_t effective; + uint32_t permitted; + uint32_t inheritable; +} lx_cap_user_data_t; + +typedef struct { + priv_set_t *p_effective; + priv_set_t *p_permitted; + priv_set_t *p_inheritable; +} lx_cap_privs_t; + +#define LX_CAP_UPDATE_PERMITTED 0x1 +#define LX_CAP_UPDATE_EFFECTIVE 0x2 +#define LX_CAP_UPDATE_INHERITABLE 0x4 + + +#define LX_CAP_MAXLEN 2 + +typedef struct { + uint32_t effective[LX_CAP_MAXLEN]; + uint32_t permitted[LX_CAP_MAXLEN]; + uint32_t inheritable[LX_CAP_MAXLEN]; +} lx_cap_data_t; + +#define LX_CAP_VERSION_1 0x19980330 +#define LX_CAP_VERSION_2 0x20071026 /* deprecated by Linux */ +#define LX_CAP_VERSION_3 0x20080522 + +#define LX_CAP_SETPCAP 8 + +/* + * Even though we lack mappings for capabilities higher than LX_CAP_MAX_VALID, + * it's valuable to test all the way out to the end of the second field. This + * ensures that new capabilities we lack support for are not silently accepted. + */ +#define LX_CAP_MAX_CHECK 63 + +#define LX_CAP_CAPISSET(id, cap) \ + (((id < 32) && (((0x1 << id) & cap[0]) != 0)) || \ + ((id >= 32) && (((0x1 << (id - 32) & cap[1]) != 0)))) + +#define LX_CAP_CAPSET(id, cap) \ + if (id < 32) { cap[0] |= (0x1 << id); } \ + else { cap[1] |= (0x1 << (id - 32)); } + +static const char *lx_cap_map_chown[] = { + PRIV_FILE_CHOWN, + PRIV_FILE_CHOWN_SELF, + NULL +}; +static const char *lx_cap_map_dac_override[] = { + PRIV_FILE_DAC_READ, + PRIV_FILE_DAC_WRITE, + PRIV_FILE_DAC_EXECUTE, + NULL +}; +static const char *lx_cap_map_dac_read_search[] = { + PRIV_FILE_DAC_SEARCH, + PRIV_FILE_DAC_READ, + NULL +}; +static const char *lx_cap_map_fowner[] = { PRIV_FILE_OWNER, NULL }; +static const char *lx_cap_map_fsetid[] = { PRIV_FILE_SETID, NULL }; +static const char *lx_cap_map_kill[] = { PRIV_PROC_OWNER, NULL }; +/* + * One way that Linux capabilities(7) differs from Illumos privileges(5) is + * that it distinguishes between setuid and setgroups rights. This will be a + * problem if an lx-branded process requests to drop only CAP_SETUID but not + * CAP_SETGID. + * + * In that case, CAP_SETUID will be maintained. + */ +static const char *lx_cap_map_setgid[] = { PRIV_PROC_SETID, NULL }; +static const char *lx_cap_map_setuid[] = { PRIV_PROC_SETID, NULL }; +static const char *lx_cap_map_linux_immutable[] = { PRIV_FILE_FLAG_SET, NULL }; +static const char *lx_cap_map_bind_service[] = { PRIV_NET_PRIVADDR, NULL }; +static const char *lx_cap_map_net_admin[] = { + PRIV_SYS_IP_CONFIG, + NULL + /* + * It would probably make sense to include PRIV_SYS_DL_CONFIG, but that + * privilege is not extended to non-global zones by default. A more + * sophisticated capabilities translation layer could make it optional. + */ +}; +static const char *lx_cap_map_net_raw[] = { + PRIV_NET_RAWACCESS, + PRIV_NET_ICMPACCESS, + NULL +}; +static const char *lx_cap_map_ipc_lock[] = { PRIV_PROC_LOCK_MEMORY, NULL }; +static const char *lx_cap_map_ipc_owner[] = { + PRIV_IPC_DAC_READ, + PRIV_IPC_DAC_WRITE, + PRIV_IPC_OWNER, + NULL +}; +static const char *lx_cap_map_sys_chroot[] = { PRIV_PROC_CHROOT, NULL }; +static const char *lx_cap_map_sys_admin[] = { + PRIV_SYS_MOUNT, + PRIV_SYS_ADMIN, + NULL +}; +static const char *lx_cap_map_sys_nice[] = { PRIV_PROC_PRIOUP, NULL }; +static const char *lx_cap_map_sys_resource[] = { PRIV_SYS_RESOURCE, NULL }; +static const char *lx_cap_map_audit_write[] = { PRIV_PROC_AUDIT, NULL }; +static const char *lx_cap_map_audit_control[] = { PRIV_SYS_AUDIT, NULL }; + +/* + * Mapping of Linux capabilities -> Illumos privileges + * The ID definitions can be found in the Linux sources here: + * include/uapi/linux/capability.h + * + * Order is critical. + */ +static const char ** lx_cap_mapping[LX_CAP_MAX_VALID + 1] = { + lx_cap_map_chown, /* CAP_CHOWN */ + lx_cap_map_dac_override, /* CAP_DAC_OVERRIDE */ + lx_cap_map_dac_read_search, /* CAP_DAC_READ_SEARCH */ + lx_cap_map_fowner, /* CAP_FOWNER */ + lx_cap_map_fsetid, /* CAP_FSETID */ + lx_cap_map_kill, /* CAP_KILL */ + lx_cap_map_setgid, /* CAP_SETGID */ + lx_cap_map_setuid, /* CAP_SETUID */ + NULL, /* CAP_SETPCAP */ + lx_cap_map_linux_immutable, /* CAP_LINUX_IMMUTABLE */ + lx_cap_map_bind_service, /* CAP_BIND_SERVICE */ + NULL, /* CAP_BROADCAST */ + lx_cap_map_net_admin, /* CAP_NET_ADMIN */ + lx_cap_map_net_raw, /* CAP_NET_RAW */ + lx_cap_map_ipc_lock, /* CAP_IPC_LOCK */ + lx_cap_map_ipc_owner, /* CAP_IPC_OWNER */ + NULL, /* CAP_MODULE */ + NULL, /* CAP_RAWIO */ + lx_cap_map_sys_chroot, /* CAP_SYS_CHROOT */ + NULL, /* CAP_PTRACE */ + NULL, /* CAP_PACCT */ + lx_cap_map_sys_admin, /* CAP_SYS_ADMIN */ + NULL, /* CAP_BOOT */ + lx_cap_map_sys_nice, /* CAP_SYS_NICE */ + lx_cap_map_sys_resource, /* CAP_SYS_RESOURCE */ + NULL, /* CAP_SYS_TIME */ + NULL, /* CAP_SYS_TTY_CONFIG */ + NULL, /* CAP_MKNOD */ + NULL, /* CAP_LEASE */ + lx_cap_map_audit_write, /* CAP_AUDIT_WRITE */ + lx_cap_map_audit_control, /* CAP_AUDIT_CONTROL */ + NULL, /* CAP_SETFCAP */ + NULL, /* CAP_MAC_OVERRIDE */ + NULL, /* CAP_MAC_ADMIN */ + NULL, /* CAP_SYSLOG */ + NULL, /* CAP_WAKE_ALARM */ + NULL /* CAP_BLOCK_SUSPEND */ +}; + +/* track priv_set_t size, set on entry to lx_capset/lx_capget */ +static unsigned int lx_cap_priv_size = 0; + +/* safely allocate priv_set_t triplet on the stack */ +#define LX_CAP_ALLOC_PRIVS(ptr) \ + { \ + ptr = SAFE_ALLOCA(sizeof (lx_cap_privs_t) + \ + (3 * lx_cap_priv_size)); \ + if (ptr != NULL) { \ + ptr->p_effective = (priv_set_t *) \ + ((caddr_t)ptr + sizeof (lx_cap_privs_t)); \ + ptr->p_permitted = (priv_set_t *) \ + ((caddr_t)ptr + sizeof (lx_cap_privs_t) + \ + lx_cap_priv_size);\ + ptr->p_inheritable = (priv_set_t *) \ + ((caddr_t)ptr + sizeof (lx_cap_privs_t) + \ + 2 * lx_cap_priv_size); \ + } \ + } + +static long +lx_cap_update_priv(priv_set_t *priv, const uint32_t cap[]) +{ + int i, j; + boolean_t cap_set; + boolean_t priv_set; + boolean_t updated = B_FALSE; + for (i = 0; i <= LX_CAP_MAX_CHECK; i++) { + cap_set = LX_CAP_CAPISSET(i, cap); + if (i > LX_CAP_MAX_VALID || lx_cap_mapping[i] == NULL) { + /* don't allow setting unsupported caps */ + if (cap_set) { + /* + * CAP_SETPCAP is a special capability, with + * varying behavior, that can be used to + * control if the process can change other + * process's capabilities, or to control moving + * capabilities between sets. For now we ignore + * this if its passed in. + */ + if (i == LX_CAP_SETPCAP) { + continue; + } + lx_unsupported("set unsupported capability %d", + i); + return (-1); + } else { + continue; + } + } + for (j = 0; lx_cap_mapping[i][j] != NULL; j++) { + priv_set = priv_ismember(priv, lx_cap_mapping[i][j]); + if (priv_set && !cap_set) { + VERIFY0(priv_delset(priv, + lx_cap_mapping[i][j])); + updated = B_TRUE; + } else if (!priv_set && cap_set) { + VERIFY0(priv_addset(priv, + lx_cap_mapping[i][j])); + updated = B_TRUE; + } + } + } + if (updated) + return (1); + else + return (0); +} + +static long +lx_cap_to_priv(lx_cap_data_t *cap, lx_cap_privs_t *priv) +{ + long changes = 0; + long result; + + result = lx_cap_update_priv(priv->p_permitted, cap->permitted); + if (result < 0) + return (-1); + else if (result > 0) + changes |= LX_CAP_UPDATE_PERMITTED; + + result = lx_cap_update_priv(priv->p_effective, cap->effective); + if (result < 0) + return (-1); + else if (result > 0) + changes |= LX_CAP_UPDATE_EFFECTIVE; + + result = lx_cap_update_priv(priv->p_inheritable, cap->inheritable); + if (result < 0) + return (-1); + else if (result > 0) + changes |= LX_CAP_UPDATE_INHERITABLE; + + return (changes); +} + +static void +lx_cap_from_priv(const priv_set_t *priv, uint32_t cap[]) +{ + int i, j; + boolean_t valid; + (void) memset(cap, '\0', sizeof (uint32_t) * LX_CAP_MAXLEN); + for (i = 0; i <= LX_CAP_MAX_VALID; i++) { + if (lx_cap_mapping[i] == NULL) { + continue; + } + valid = B_TRUE; + for (j = 0; lx_cap_mapping[i][j] != NULL; j++) { + if (!priv_ismember(priv, + lx_cap_mapping[i][j])) { + valid = B_FALSE; + } + } + if (valid) { + LX_CAP_CAPSET(i, cap); + } + } +} + +static long +lx_cap_read_cap(const lx_cap_user_header_t *uhp, const lx_cap_user_data_t *udp, + lx_cap_data_t *cd) +{ + lx_cap_user_header_t uh; + lx_cap_user_data_t ud_buf; + int cap_count; + int i; + + if (uucopy(uhp, &uh, sizeof (uh)) != 0) + return (-errno); + + switch (uh.version) { + case LX_CAP_VERSION_1: + cap_count = 1; + break; + case LX_CAP_VERSION_2: + case LX_CAP_VERSION_3: + cap_count = 2; + break; + default: + return (-EINVAL); + } + + /* Only allow capset on calling process */ + if (uh.pid != 0 && uh.pid != getpid()) + return (-EPERM); + + /* zero the struct in case cap_count < 2 */ + (void) memset(cd, '\0', sizeof (lx_cap_data_t)); + + for (i = 0; i < cap_count; i++) { + if (uucopy(udp + i, &ud_buf, sizeof (ud_buf)) != 0) + return (-errno); + cd->permitted[i] = ud_buf.permitted; + cd->effective[i] = ud_buf.effective; + cd->inheritable[i] = ud_buf.inheritable; + } + return (0); +} + +long +lx_capget(uintptr_t p1, uintptr_t p2) +{ + const priv_impl_info_t *impl; + lx_cap_user_header_t *uhp = (lx_cap_user_header_t *)p1; + lx_cap_user_data_t *udp = (lx_cap_user_data_t *)p2; + lx_cap_user_header_t uh; + lx_cap_privs_t *privs; + lx_cap_data_t cd_result; + lx_cap_user_data_t cd_buf; + int cap_count; + int i; + + if (lx_cap_priv_size == 0) { + impl = getprivimplinfo(); + lx_cap_priv_size = sizeof (priv_chunk_t) * impl->priv_setsize; + } + + if (uucopy(uhp, &uh, sizeof (uh)) != 0) + return (-errno); + + switch (uh.version) { + case LX_CAP_VERSION_1: + cap_count = 1; + break; + case LX_CAP_VERSION_2: + case LX_CAP_VERSION_3: + cap_count = 2; + break; + default: + /* + * As per the man page: call will fail with EINVAL and set the + * version field of the header to the kernel preferred version + * when an unsupported version value is provided. + */ + uh.version = LX_CAP_VERSION_3; + if (uucopy(&uh, uhp, sizeof (uh)) != 0) + return (-errno); + return (-EINVAL); + } + + /* + * Only allow capget on the calling process. + * If a pid is specified, lie about being able to locate it. + */ + if (uh.pid > 0 && uh.pid != getpid()) + return (-ESRCH); + if (uh.pid < 0) + return (-EINVAL); + + LX_CAP_ALLOC_PRIVS(privs) + + if (privs == NULL) + return (-ENOMEM); + + if (getppriv(PRIV_PERMITTED, privs->p_permitted) != 0) + return (-errno); + if (getppriv(PRIV_EFFECTIVE, privs->p_effective) != 0) + return (-errno); + if (getppriv(PRIV_INHERITABLE, privs->p_inheritable) != 0) + return (-errno); + + lx_cap_from_priv(privs->p_permitted, cd_result.permitted); + lx_cap_from_priv(privs->p_effective, cd_result.effective); + lx_cap_from_priv(privs->p_inheritable, cd_result.inheritable); + + /* convert to output format */ + for (i = 0; i < cap_count; i++) { + cd_buf.effective = cd_result.effective[i]; + cd_buf.permitted = cd_result.permitted[i]; + cd_buf.inheritable = cd_result.inheritable[i]; + if (uucopy(&cd_buf, udp + i, sizeof (cd_buf)) != 0) + return (-errno); + } + + return (0); +} + +long +lx_capset(uintptr_t p1, uintptr_t p2) +{ + const priv_impl_info_t *impl; + lx_cap_data_t cd; + lx_cap_privs_t *privs; + long result; + + if (lx_cap_priv_size == 0) { + impl = getprivimplinfo(); + lx_cap_priv_size = sizeof (priv_chunk_t) * impl->priv_setsize; + } + + /* verify header and read in desired capabilities */ + result = lx_cap_read_cap((lx_cap_user_header_t *)p1, + (lx_cap_user_data_t *)p2, &cd); + if (result != 0) + return (result); + + LX_CAP_ALLOC_PRIVS(privs) + + if (privs == NULL) + return (-ENOMEM); + + /* fetch current privs to compare against */ + if (getppriv(PRIV_PERMITTED, privs->p_permitted) != 0) + return (-errno); + if (getppriv(PRIV_EFFECTIVE, privs->p_effective) != 0) + return (-errno); + if (getppriv(PRIV_INHERITABLE, privs->p_inheritable) != 0) + return (-errno); + + + result = lx_cap_to_priv(&cd, privs); + if (result < 0) + return (-EPERM); + + /* report success if no changes needed */ + if (result == 0) + return (0); + + /* Ensure the effective/inheritable caps aren't > permitted */ + if (!priv_issubset(privs->p_effective, privs->p_permitted) || + !priv_issubset(privs->p_inheritable, privs->p_permitted)) + return (-EPERM); + + /* + * Here is where things become racy. Linux updates all three + * capability sets simultaneously in the capset syscall. In order to + * emulate capabilities via privileges, three setppriv operations are + * required in sequence. If one or two should fail, there is not a + * mechanism to convey the incomplete operation to the caller. + * + * We do two things to make this less risky: + * 1. Verify that both the desired effective and inheritable + * sets are subsets of the desired permitted set. + * 2. Perform the setppriv of the permitted set first. + * + * Should the setppriv(permitted) fail, we can safely bail out with an + * error. If it succeeds, the setppriv of effective and inheritable + * are likely to succeed given that they've been verified legal. + * + * If the partial error does happen, we'll be forced to report failure + * even though the privileges were altered. + */ + + if ((result & LX_CAP_UPDATE_PERMITTED) != 0) { + /* failure here is totally safe */ + if (setppriv(PRIV_SET, PRIV_PERMITTED, privs->p_permitted) != 0) + return (-errno); + } + if ((result & LX_CAP_UPDATE_EFFECTIVE) != 0) { + /* failure here is a bummer */ + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privs->p_effective) != 0) + return (-errno); + } + if ((result & LX_CAP_UPDATE_EFFECTIVE) != 0) { + /* failure here is a major bummer */ + if (setppriv(PRIV_SET, PRIV_INHERITABLE, + privs->p_inheritable) != 0) + return (-errno); + } + + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/clock.c b/usr/src/lib/brand/lx/lx_brand/common/clock.c new file mode 100644 index 0000000000..e627df68dc --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/clock.c @@ -0,0 +1,192 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. + */ + +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/resource.h> +#include <sys/timerfd.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> +#include <lx_signum.h> + +/* + * Translating from the Linux clock types to the illumos types is a bit of a + * mess. + * + * Linux uses different values for it clock identifiers, so we have to do basic + * translations between the two. Thankfully, both Linux and illumos implement + * the same POSIX SUSv3 clock types, so the semantics should be identical. + * + * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two + * clock backends currently implemented on illumos. Functions in the kernel + * that use the CLOCK_BACKEND macro will return an error for any clock type + * that does not exist in the clock_backend array. These functions are + * clock_settime, clock_gettime, clock_getres and timer_create. + * + * For reference, the kernel's clock_backend array looks like this: + * + * clock_backend[CLOCK_MAX] (6 entries) + * 0 __CLOCK_REALTIME0 valid ptr. (obs. same as CLOCK_REALTIME) + * 1 CLOCK_VIRTUAL NULL + * 2 CLOCK_THREAD_CPUTIME_ID NULL + * 3 CLOCK_REALTIME valid ptr. + * 4 CLOCK_MONOTONIC (CLOCK_HIGHRES) valid ptr. + * 5 CLOCK_PROCESS_CPUTIME_ID NULL + */ + +#define CLOCK_RT_SLOT 0 + +#define LX_CLOCK_REALTIME 0 +#define LX_CLOCK_MONOTONIC 1 + +/* + * Limits for a minimum interval are enforced when creating timers from the + * CLOCK_HIGHRES source. Values below this minimum will be clamped if the + * process lacks the proc_clock_highres privilege. + */ +static int ltos_clock[] = { + CLOCK_REALTIME, /* LX_CLOCK_REALTIME */ + CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC */ + CLOCK_PROCESS_CPUTIME_ID, /* LX_CLOCK_PROCESS_CPUTIME_ID */ + CLOCK_THREAD_CPUTIME_ID, /* LX_CLOCK_THREAD_CPUTIME_ID */ + CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC_RAW */ + CLOCK_REALTIME, /* LX_CLOCK_REALTIME_COARSE */ + CLOCK_HIGHRES /* LX_CLOCK_MONOTONIC_COARSE */ +}; + +#define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0])) + + +long +lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp, + struct timespec *rmtp) +{ + int ret = 0; + int err; + struct timespec rqt, rmt; + + if (clock < 0 || clock >= LX_CLOCK_MAX) + return (-EINVAL); + + if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0) + return (-EFAULT); + + /* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */ + if ((err = clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt)) + != 0) { + if (err != EINTR) + return (-err); + ret = -EINTR; + /* + * We fall through in case we have to pass back the remaining + * time. + */ + } + + /* + * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is + * non-NULL. + */ + if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) && + (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0)) + return (-EFAULT); + + return (ret); +} + +/*ARGSUSED*/ +long +lx_adjtimex(void *tp) +{ + return (-EPERM); +} + +long +lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val, + struct itimerspec *old_val) +{ + return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0); +} + +long +lx_timer_gettime(timer_t tid, struct itimerspec *val) +{ + return ((timer_gettime(tid, val) < 0) ? -errno : 0); +} + +long +lx_timer_getoverrun(timer_t tid) +{ + int val; + + val = timer_getoverrun(tid); + return ((val < 0) ? -errno : val); +} + +long +lx_timer_delete(timer_t tid) +{ + return ((timer_delete(tid) < 0) ? -errno : 0); +} + +long +lx_timerfd_create(int clockid, int flags) +{ + int r; + + /* These are the only two valid values. LTP tests for this. */ + if (clockid != LX_CLOCK_REALTIME && clockid != LX_CLOCK_MONOTONIC) + return (-EINVAL); + + r = timerfd_create(ltos_clock[clockid], flags); + /* + * As with the eventfd case, we return a slightly less jarring + * error condition if we cannot open /dev/timerfd. + */ + if (r == -1 && errno == ENOENT) + return (-ENOTSUP); + + return (r == -1 ? -errno : r); +} + +long +lx_timerfd_settime(int fd, int flags, const struct itimerspec *value, + struct itimerspec *ovalue) +{ + int r = timerfd_settime(fd, flags, value, ovalue); + + return (r == -1 ? -errno : r); +} + +long +lx_timerfd_gettime(int fd, struct itimerspec *value) +{ + int r = timerfd_gettime(fd, value); + + return (r == -1 ? -errno : r); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/clone.c b/usr/src/lib/brand/lx/lx_brand/common/clone.c new file mode 100644 index 0000000000..c6048281b5 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/clone.c @@ -0,0 +1,724 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <ucontext.h> +#include <thread.h> +#include <strings.h> +#include <libintl.h> +#include <sys/regset.h> +#include <sys/syscall.h> +#include <sys/inttypes.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/segments.h> +#include <signal.h> +#include <sys/lx_misc.h> +#include <sys/lx_types.h> +#include <sys/lx_signal.h> +#include <sys/lx_syscall.h> +#include <sys/lx_brand.h> +#include <sys/lx_debug.h> +#include <sys/lx_thread.h> +#include <sys/fork.h> +#include <sys/mman.h> +#include <sys/debug.h> +#include <lx_syscall.h> + +#define CLONE_VFORK (LX_CLONE_VM | LX_CLONE_VFORK) +#define CLONE_TD (LX_CLONE_THREAD|LX_CLONE_DETACH) + +#define IS_FORK(f) (((f) & SHARED_AS) == 0) +#define IS_VFORK(f) (((f) & CLONE_VFORK) == CLONE_VFORK) + +/* + * This is dicey. This seems to be an internal glibc structure, and not + * part of any external interface. Thus, it is subject to change without + * notice. FWIW, clone(2) itself seems to be an internal (or at least + * unstable) interface, since strace(1) shows it differently than the man + * page. + */ +struct lx_desc +{ + uint32_t entry_number; + uint32_t base_addr; + uint32_t limit; + uint32_t seg_32bit:1; + uint32_t contents:2; + uint32_t read_exec_only:1; + uint32_t limit_in_pages:1; + uint32_t seg_not_present:1; + uint32_t useable:1; + uint32_t empty:25; +}; + +struct clone_state { + void *c_retaddr; /* instr after clone()'s int80 */ + int c_flags; /* flags to clone(2) */ + int c_sig; /* signal to send on thread exit */ + void *c_stk; /* %esp of new thread */ + void *c_ptidp; + struct lx_desc *c_ldtinfo; /* thread-specific segment */ + void *c_ctidp; + ucontext_t c_uc; /* original register state/sigmask */ + volatile int *c_clone_res; /* pid/error returned to cloner */ + int c_ptrace_event; /* ptrace(2) event for child stop */ + void *c_ntv_stk; /* native stack for this thread */ + size_t c_ntv_stk_sz; /* native stack size */ + lx_tsd_t *c_lx_tsd; /* tsd area for thread */ +}; + +long +lx_exit(uintptr_t p1) +{ + int status = (int)p1; + lx_tsd_t *lx_tsd = lx_get_tsd(); + + /* + * If we are a vfork(2)ed child, we need to exit as quickly and + * cleanly as possible to avoid corrupting our parent. + */ + if (lx_tsd->lxtsd_is_vforked != 0) { + _exit(status); + } + + lx_tsd->lxtsd_exit = LX_ET_EXIT; + lx_tsd->lxtsd_exit_status = status; + + lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEEXIT, B_FALSE, + (ulong_t)status, NULL); + + /* + * This thread is exiting. Restore the state of the thread to + * what it was before we started running linux code. + */ + (void) setcontext(&lx_tsd->lxtsd_exit_context); + + /* + * If we returned from the setcontext(2), something is very wrong. + */ + lx_err_fatal("exit: unable to set exit context: %s", strerror(errno)); + + /*NOTREACHED*/ + return (0); +} + +long +lx_group_exit(uintptr_t p1) +{ + int status = (int)p1; + lx_tsd_t *lx_tsd = lx_get_tsd(); + + /* + * If we are a vfork(2)ed child, we need to exit as quickly and + * cleanly as possible to avoid corrupting our parent. + */ + if (lx_tsd->lxtsd_is_vforked != 0) { + _exit(status); + } + + lx_tsd->lxtsd_exit = LX_ET_EXIT_GROUP; + lx_tsd->lxtsd_exit_status = status; + + /* + * This thread is exiting. Restore the state of the thread to + * what it was before we started running linux code. + */ + (void) setcontext(&lx_tsd->lxtsd_exit_context); + + /* + * If we returned from the setcontext(2), something is very wrong. + */ + lx_err_fatal("group_exit: unable to set exit context: %s", + strerror(errno)); + + /*NOTREACHED*/ + return (0); +} + +static void * +clone_start(void *arg) +{ + int rval; + struct clone_state *cs = (struct clone_state *)arg; + lx_tsd_t *lxtsd; + + /* + * Let the kernel finish setting up all the needed state for this + * new thread. + * + * We already created the thread using the thr_create(3C) library + * call, so most of the work required to emulate lx_clone(2) has + * been done by the time we get to this point. + */ + lx_debug("\tre-vectoring to lx kernel module to complete lx_clone()"); + lx_debug("\tB_HELPER_CLONE(0x%x, 0x%p, 0x%p, 0x%p)", + cs->c_flags, cs->c_ptidp, cs->c_ldtinfo, cs->c_ctidp); + + rval = syscall(SYS_brand, B_HELPER_CLONE, cs->c_flags, cs->c_ptidp, + cs->c_ldtinfo, cs->c_ctidp); + + /* + * At this point the parent is waiting for cs->c_clone_res to go + * non-zero to indicate the thread has been cloned. The value set + * in cs->c_clone_res will be used for the return value from + * clone(). + */ + if (rval < 0) { + *(cs->c_clone_res) = -errno; + lx_debug("\tkernel clone failed, errno %d\n", errno); + free(cs->c_lx_tsd); + free(cs); + return (NULL); + } + + /* + * Initialize the thread specific data for this thread. + */ + lxtsd = cs->c_lx_tsd; + lx_init_tsd(lxtsd); + lxtsd->lxtsd_clone_state = cs; + + /* + * Install the emulation stack for this thread. Register the + * thread-specific data structure with the stack list so that it may be + * freed at thread exit or fork(2). + */ + lx_install_stack(cs->c_ntv_stk, cs->c_ntv_stk_sz, lxtsd); + + /* + * Let the parent know that the clone has (effectively) been + * completed. + */ + *(cs->c_clone_res) = rval; + + /* + * We want to load the general registers from this context, restore the + * original signal mask, and switch to the BRAND stack. The original + * signal mask was saved to the context by lx_clone(). + */ + cs->c_uc.uc_flags = UC_CPU | UC_SIGMASK; + cs->c_uc.uc_brand_data[0] = (void *)LX_UC_STACK_BRAND; + + /* + * New threads will not link into the existing context chain. + */ + cs->c_uc.uc_link = NULL; + + /* + * Set stack pointer and entry point for new thread: + */ + LX_REG(&cs->c_uc, REG_SP) = (uintptr_t)cs->c_stk; + LX_REG(&cs->c_uc, REG_PC) = (uintptr_t)cs->c_retaddr; + + /* + * Return 0 to the child: + */ + LX_REG(&cs->c_uc, REG_R0) = (uintptr_t)0; + + /* + * Fire the ptrace(2) event stop in the new thread: + */ + lx_ptrace_stop_if_option(cs->c_ptrace_event, B_TRUE, 0, &cs->c_uc); + + /* + * Jump to the Linux process. This call cannot return. + */ + lx_jump_to_linux(&cs->c_uc); + /* NOTREACHED */ +} + +/* + * The way Linux handles stopping for FORK vs. CLONE does not map exactly to + * which syscall was used. Instead, it has to do with which signal is set in + * the low byte of the clone flag. The only time the CLONE event is emitted is + * if the clone signal (the low byte of the flags argument) is set to something + * other than SIGCHLD (see the Linux src in kernel/fork.c do_fork() for the + * actual code). + */ +static int +ptrace_clone_event(int flags) +{ + if (flags & LX_CLONE_VFORK) + return (LX_PTRACE_O_TRACEVFORK); + + if ((flags & LX_CSIGNAL) != LX_SIGCHLD) + return (LX_PTRACE_O_TRACECLONE); + + return (LX_PTRACE_O_TRACEFORK); +} + +/* + * See glibc sysdeps/unix/sysv/linux/x86_64/clone.S code for x64 argument order + * and the Linux kernel/fork.c code for the various ways arguments can be passed + * to the clone syscall (CONFIG_CLONE_BACKWARDS, et al). + */ +long +lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5) +{ + struct clone_state *cs; + int flags = (int)p1; + void *cldstk = (void *)p2; + void *ptidp = (void *)p3; +#if defined(_LP64) + void *ctidp = (void *)p4; + struct lx_desc *ldtinfo = (void *)p5; +#else /* is 32bit */ + struct lx_desc *ldtinfo = (void *)p4; + void *ctidp = (void *)p5; +#endif + thread_t tid; + volatile int clone_res; + int sig; + int rval; + int pid; + ucontext_t *ucp; + sigset_t sigmask, osigmask; + int fork_flags = 0; + int ptrace_event; + lx_tsd_t *lx_tsd = lx_get_tsd(); + + if (flags & LX_CLONE_SETTLS) { + lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p ldt=0x%p " + "ctidp=0x%p", flags, cldstk, ptidp, ldtinfo, ctidp); + } else { + lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p)", + flags, cldstk, ptidp); + } + + /* + * Only supported for pid 0 on Linux after version 2.3.21, and + * apparently not at all since 2.5.16. + */ + if (flags & LX_CLONE_PID) + return (-EINVAL); + + /* + * CLONE_THREAD requires CLONE_SIGHAND. + * + * CLONE_THREAD and CLONE_DETACHED must both be either set or cleared + * in kernel 2.4 and prior. + * In kernel 2.6 (and later) CLONE_DETACHED was dropped completely, so + * we no longer have this requirement. + */ + + if (flags & CLONE_TD) { + if (!(flags & LX_CLONE_SIGHAND)) + return (-EINVAL); + if (strncmp(lx_release, "2.4", 3) == 0 && + (flags & CLONE_TD) != CLONE_TD) + return (-EINVAL); + } + + if ((flags & LX_CLONE_NS_UNSUP) != 0) { + lx_unsupported("clone(2) no namespace support " + "(flags:0x%08X)\n", flags); + /* + * When the "kernel" does not support namespaces, applications + * (e.g. chromium) expect EINVAL, not ENOTSUP. + */ + return (-EINVAL); + } + + ucp = lx_syscall_regs(); + + /* test if pointer passed by user are writable */ + if (flags & LX_CLONE_PARENT_SETTID) { + if (uucopy(ptidp, &pid, sizeof (int)) != 0) + return (-EFAULT); + if (uucopy(&pid, ptidp, sizeof (int)) != 0) + return (-EFAULT); + } + if (flags & LX_CLONE_CHILD_SETTID) { + if (uucopy(ctidp, &pid, sizeof (int)) != 0) + return (-EFAULT); + if (uucopy(&pid, ctidp, sizeof (int)) != 0) + return (-EFAULT); + } + + ptrace_event = ptrace_clone_event(flags); + + /* + * Inform the in-kernel ptrace(2) subsystem that we are about to + * emulate a fork(2), vfork(2) or clone(2) system call. + */ + lx_ptrace_clone_begin(ptrace_event, !!(flags & LX_CLONE_PTRACE), flags); + + /* + * Handle a fork(2) operation here. If this is not a fork, a new + * thread will be created after this block. We can also create a new + * clone-group here (when two or more processes share data represented + * by a subset of the SHARED_AS flags, but not a true thread). + */ + if (IS_FORK(flags) || IS_VFORK(flags) || LX_IS_CLONE_GRP(flags)) { + if (flags & LX_CLONE_PARENT) { + lx_unsupported("clone(2) only supports CLONE_PARENT " + "for threads.\n"); + return (-ENOTSUP); + } + + if ((flags & LX_CSIGNAL) == 0) + fork_flags |= FORK_NOSIGCHLD; + + /* + * Suspend signal delivery, run the stack management prefork + * handler and perform the actual fork(2) operation. + * + * During vfork, Linux will not deliver any signals to any + * thread in the parent. Some applications (e.g. Go) depend on + * this. For example, we must prevent the following sequence: + * 1) Parent with many threads, one thread calls vfork + * 2) vforked child resets all signal handlers + * 3) a different child of the parent exits and SIGCHLD is sent + * to parent before the vforked child execs/exits + * The parent cannot receive the SIGCHLD until afer we repair + * the parent's signal handlers in lx_sighandlers_restore, once + * the parent resumes after the vfork. + */ + if (flags & LX_CLONE_VFORK) { + lx_block_all_signals(); + } else { + _sigoff(); + } + lx_stack_prefork(); + if (flags & LX_CLONE_VFORK) { + lx_sighandlers_t saved; + + /* + * Because we keep our signal disposition at user-land + * (and in memory), we must prevent it from being + * clobbered should our vforked child change the + * disposition (e.g., via sigaction()) before releasing + * the address space. We preserve our disposition by + * taking a snapshot of it before the vfork and + * restoring it afterwards -- which we can get away + * with because we know that we aren't executing + * concurrently with our child. + */ + lx_sighandlers_save(&saved); + lx_tsd->lxtsd_is_vforked++; + rval = vforkx(fork_flags); + if (rval != 0) { + lx_tsd->lxtsd_is_vforked--; + lx_sighandlers_restore(&saved); + } + } else { + rval = forkx(fork_flags); + } + + /* + * The parent process returns through the regular system call + * path here. + */ + if (rval != 0) { + /* + * Run the stack management postfork handler in the + * parent. In the CLONE_VFORK case, where it only + * needs to be performed once due to the shared address + * space, it is critical that this step is performed in + * the parent and not the child. The latter can result + * in un-woken threads blocked on lx_stack_list_lock. + */ + lx_stack_postfork(); + + /* + * Since we've already forked, we can't do much if + * uucopy fails, so we just ignore failure. Failure is + * unlikely since we've tested the memory before we did + * the fork. + */ + if (rval > 0 && (flags & LX_CLONE_PARENT_SETTID)) { + (void) uucopy(&rval, ptidp, sizeof (int)); + } + + /* + * Re-enable signal delivery in the parent process. + */ + if (flags & LX_CLONE_VFORK) { + lx_unblock_all_signals(); + } else { + _sigon(); + } + + if (rval > 0) { + lx_ptrace_stop_if_option(ptrace_event, B_FALSE, + (ulong_t)rval, NULL); + } + + return ((rval < 0) ? -errno : rval); + } + + /* + * The rest of this block runs only within the new child + * process. + */ + + if (!IS_VFORK(flags)) { + /* + * For non-vfork children run the stack management + * postfork handler. + */ + lx_stack_postfork(); + + /* + * We must free the stacks and thread-specific data + * objects for every thread except the one duplicated + * from the parent by forkx(). + */ + lx_free_other_stacks(); + } + + if (rval == 0 && (flags & LX_CLONE_CHILD_SETTID)) { + /* + * lx_getpid should not fail, and if it does, there's + * not much we can do about it since we've already + * forked, so on failure, we just don't copy the + * memory. + */ + pid = syscall(SYS_brand, B_GETPID); + if (pid >= 0) + (void) uucopy(&pid, ctidp, sizeof (int)); + } + + /* + * Set up additional data in the lx_proc_data structure as + * necessary. + */ + if ((rval = syscall(SYS_brand, B_HELPER_CLONE, flags, ptidp, + ldtinfo, ctidp)) < 0) { + return (rval); + } + + if (IS_VFORK(flags)) { + ucontext_t vforkuc; + + /* + * The vfork(2) interface is somewhat less than ideal. + * The unfortunate notion of borrowing the address + * space of the parent process requires us to jump + * through several hoops to prevent corrupting parent + * emulation state. + * + * When returning in the child, we make a copy of the + * system call return context and discard three pages + * of the native stack. Returning normally would + * clobber the native stack frame in which the brand + * library in the parent process is presently waiting. + * + * The calling program is expected to correctly use + * this dusty, underspecified relic. Neglecting to + * immediately call execve(2) or exit(2) is not + * cricket; this stack space will be permanently lost, + * not to mention myriad other undefined behaviour. + */ + bcopy(ucp, &vforkuc, sizeof (vforkuc)); + vforkuc.uc_brand_data[1] = + (caddr_t)vforkuc.uc_brand_data[1] - + LX_NATIVE_STACK_VFORK_GAP; + vforkuc.uc_link = NULL; + + lx_debug("\tvfork native stack sp %p", + vforkuc.uc_brand_data[1]); + + /* + * If provided, the child needs its new stack set up. + */ + if (cldstk != 0) { + lx_debug("\tvfork cldstk %p", cldstk); + LX_REG(&vforkuc, REG_SP) = (uintptr_t)cldstk; + } + + /* + * Re-enable signal delivery in the child process. + */ + lx_unblock_all_signals(); + + /* + * Stop for ptrace if required. + */ + lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL); + + /* + * Return to the child via the specially constructed + * vfork(2) context. + */ + LX_EMULATE_RETURN(&vforkuc, LX_SYS_clone, 0, 0); + (void) syscall(SYS_brand, B_EMULATION_DONE, &vforkuc, + LX_SYS_clone, 0, 0); + + assert(0); + } + + /* + * If provided, the child needs its new stack set up. + */ + if (cldstk != 0) { + lx_debug("\tcldstk %p", cldstk); + LX_REG(ucp, REG_SP) = (uintptr_t)cldstk; + } + + /* + * Re-enable signal delivery in the child process. + */ + if (flags & LX_CLONE_VFORK) { + lx_unblock_all_signals(); + } else { + _sigon(); + } + + /* + * Stop for ptrace if required. + */ + lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL); + + /* + * The child process returns via the regular emulated system + * call path: + */ + return (0); + } + + /* + * A supported clone-group was handled above, so now it must be a + * true native thread, which means exactly these flags are supported + */ + if (((flags & SHARED_AS) != SHARED_AS)) { + lx_unsupported("clone(2) a thread requires that all or none of " + "CLONE_VM/FS/FILES/THREAD/SIGHAND be set. (flags:0x%08X)\n", + flags); + return (-ENOTSUP); + } + + if (cldstk == NULL) { + lx_unsupported("clone(2) requires the caller to allocate the " + "child's stack.\n"); + return (-ENOTSUP); + } + + /* + * If we want a signal-on-exit, ensure that the signal is valid. + */ + if ((sig = ltos_signo[flags & LX_CSIGNAL]) == -1) { + lx_unsupported("clone(2) passed unsupported signal: %d", sig); + return (-ENOTSUP); + } + + /* + * Initialise the state structure we pass as an argument to the new + * thread: + */ + if ((cs = malloc(sizeof (*cs))) == NULL) { + lx_debug("could not allocate clone_state: %d", errno); + return (-ENOMEM); + } + cs->c_flags = flags; + cs->c_sig = sig; + cs->c_stk = cldstk; + cs->c_ptidp = ptidp; + cs->c_ldtinfo = ldtinfo; + cs->c_ctidp = ctidp; + cs->c_clone_res = &clone_res; + cs->c_ptrace_event = ptrace_event; + /* + * We want the new thread to return directly to the call site for + * the system call. + */ + cs->c_retaddr = (void *)LX_REG(ucp, REG_PC); + /* + * Copy the saved context for the clone(2) system call so that the + * new thread may use it to initialise registers. + */ + bcopy(ucp, &cs->c_uc, sizeof (cs->c_uc)); + if ((cs->c_lx_tsd = malloc(sizeof (*cs->c_lx_tsd))) == NULL) { + free(cs); + return (-ENOMEM); + } + + clone_res = 0; + + /* + * Block all signals because the thread we create won't be able to + * properly handle them until it's fully set up. + */ + VERIFY0(sigfillset(&sigmask)); + if (sigprocmask(SIG_BLOCK, &sigmask, &osigmask) < 0) { + lx_debug("lx_clone sigprocmask() failed: %d", errno); + free(cs->c_lx_tsd); + free(cs); + return (-errno); + } + cs->c_uc.uc_sigmask = osigmask; + + /* + * Allocate the native stack for this new thread now, so that we + * can return failure gracefully as ENOMEM. + */ + if (lx_alloc_stack(&cs->c_ntv_stk, &cs->c_ntv_stk_sz) != 0) { + free(cs->c_lx_tsd); + free(cs); + return (-ENOMEM); + } + + rval = thr_create(NULL, 0, clone_start, cs, THR_DETACHED, &tid); + + /* + * If the thread did not start, free the resources we allocated: + */ + if (rval != 0) { + (void) munmap(cs->c_ntv_stk, cs->c_ntv_stk_sz); + free(cs->c_lx_tsd); + free(cs); + } + + /* + * Release any pending signals + */ + (void) sigprocmask(SIG_SETMASK, &osigmask, NULL); + + /* + * Wait for the child to be created and have its tid assigned. + */ + if (rval == 0) { + while (clone_res == 0) + ; + + rval = clone_res; + lx_ptrace_stop_if_option(ptrace_event, B_FALSE, (ulong_t)rval, + NULL); + + return (rval); + } else { + /* + * Return the error from thr_create(3C). + */ + return (-rval); + } +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/debug.c b/usr/src/lib/brand/lx/lx_brand/common/debug.c new file mode 100644 index 0000000000..a8f994c43d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/debug.c @@ -0,0 +1,171 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <thread.h> +#include <unistd.h> + +#include <sys/modctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <sys/lx_brand.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> + +/* internal debugging state */ +static const char *lx_debug_path = NULL; /* debug output file path */ +static char lx_debug_path_buf[MAXPATHLEN]; + +int lx_dtrace_lazyload = 1; /* patchable; see below */ + +void +lx_debug_init(boolean_t do_dtrace, boolean_t dbg_enable, const char *dbg_file) +{ + /* + * Our DTrace USDT provider is loaded in our .init section, which is + * not run by our e_entry ELF entry point (_start, which calls into + * lx_init()). We exploit this to only actually load our USDT provider + * if LX_DTRACE is set, assuring that we don't compromise fork() + * performance in the (common) case that DTrace of lx_brand.so.1 itself + * isn't enabled or desired. (As with all USDT providers, it can always + * be loaded by explicitly specifying the full provider name). Note + * that we also allow this behavior to be set via a manual override, + * lx_dtrace_lazyload -- allowing for USDT probes to be automatically + * provided in situations where setting an environment variable is + * tedious or otherwise impossible. + */ + if (do_dtrace || !lx_dtrace_lazyload) { + extern void _init(void); + _init(); + } + + if (!dbg_enable) + return; + + /* + * It's OK to use this value without any locking, as all callers can + * use the return value to decide whether extra work should be done + * before calling lx_debug(). + * + * If debugging is disabled after a routine calls this function it + * doesn't really matter as lx_debug() will see debugging is disabled + * and will not output anything. + */ + lx_debug_enabled = 1; + + /* check if there's a debug log file specified */ + lx_debug_path = dbg_file; + if (lx_debug_path == NULL) { + /* send all debugging output to /dev/tty */ + lx_debug_path = "/dev/tty"; + } + + (void) strlcpy(lx_debug_path_buf, lx_debug_path, + sizeof (lx_debug_path_buf)); + lx_debug_path = lx_debug_path_buf; + + lx_debug("lx_debug: debugging output ENABLED to path: \"%s\"", + lx_debug_path); +} + +void +lx_debug(const char *msg, ...) +{ + va_list ap; + char *buf; + int rv, fd, n; + int errno_backup; + int size = LX_MSG_MAXLEN + 1; + + if (lx_debug_enabled == 0 && !LX_DEBUG_ENABLED()) + return; + + /* + * If debugging is not enabled, we do not wish to have a large stack + * footprint. The buffer allocation is thus done conditionally, + * rather than as regular automatic storage. + */ + if ((buf = SAFE_ALLOCA(size)) == NULL) + return; + + errno_backup = errno; + + /* prefix the message with pid/tid */ + if ((n = snprintf(buf, size, "%u/%u: ", getpid(), thr_self())) == -1) { + errno = errno_backup; + return; + } + + /* format the message */ + va_start(ap, msg); + rv = vsnprintf(&buf[n], size - n, msg, ap); + va_end(ap); + if (rv == -1) { + errno = errno_backup; + return; + } + + /* add a carrige return if there isn't one already */ + if ((buf[strlen(buf) - 1] != '\n') && + (strlcat(buf, "\n", size) >= size)) { + errno = errno_backup; + return; + } + + LX_DEBUG(buf); + + if (!lx_debug_enabled) + return; + + /* + * Open the debugging output file. note that we don't protect + * ourselves against exec or fork1 here. if an mt process were + * to exec/fork1 while we're doing this they'd end up with an + * extra open desciptor in their fd space. a'well. shouldn't + * really matter. + */ + if ((fd = open(lx_debug_path, + O_WRONLY|O_APPEND|O_CREAT|O_NDELAY|O_NOCTTY, 0666)) == -1) { + return; + } + (void) fchmod(fd, 0666); + + /* we retry in case of EINTR */ + do { + rv = write(fd, buf, strlen(buf)); + } while ((rv == -1) && (errno == EINTR)); + (void) fsync(fd); + + (void) close(fd); + errno = errno_backup; +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/dir.c b/usr/src/lib/brand/lx/lx_brand/common/dir.c new file mode 100644 index 0000000000..ed10dfb822 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/dir.c @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#include <string.h> +#include <stddef.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/dirent.h> +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> +#include <sys/lx_syscall.h> + +#define LX_NAMEMAX 256 + +struct lx_old_dirent { + long d_ino; /* not l_ino_t */ + long d_off; + ushort_t d_reclen; + char d_name[LX_NAMEMAX]; +}; + +/* + * Read in one dirent structure from fd into dirp. + * p3 (count) is ignored. + */ +/*ARGSUSED*/ +long +lx_readdir(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int fd = (int)p1; + struct lx_old_dirent *dirp = (struct lx_old_dirent *)p2; + uint_t count = sizeof (struct lx_old_dirent); + int rc = 0; + struct lx_old_dirent _ld; + struct dirent *sd = (struct dirent *)&_ld; + + /* + * The return value from getdents is not applicable, as + * it might have squeezed more than one dirent in the buffer + * we provided. + * + * getdents() will deal with the case of dirp == NULL + */ + if ((rc = getdents(fd, sd, count)) < 0) + return (-errno); + + /* + * Set rc 1 (pass), or 0 (end of directory). + */ + rc = (sd->d_reclen == 0) ? 0 : 1; + + if (uucopy(sd, dirp, count) != 0) + return (-errno); + + return (rc); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/file.c b/usr/src/lib/brand/lx/lx_brand/common/file.c new file mode 100644 index 0000000000..a1f55e2899 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/file.c @@ -0,0 +1,347 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. + */ + +#include <sys/fstyp.h> +#include <sys/fsid.h> + +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/vnode.h> +#include <fcntl.h> +#include <string.h> +#include <utime.h> +#include <atomic.h> +#include <sys/syscall.h> + +#include <sys/lx_syscall.h> +#include <sys/lx_types.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_fcntl.h> + +#define LX_UTIME_NOW ((1l << 30) - 1l) +#define LX_UTIME_OMIT ((1l << 30) - 2l) + +static int +install_checkpath(uintptr_t p1) +{ + int saved_errno = errno; + char path[MAXPATHLEN]; + + /* + * The "dev" RPM package wants to modify /dev/pts, but /dev/pts is a + * lofs mounted copy of /native/dev/pts, so that won't work. + * + * Instead, if we're trying to modify /dev/pts from install mode, just + * act as if it succeded. + */ + if (uucopystr((void *)p1, path, MAXPATHLEN) == -1) + return (-errno); + + if (strcmp(path, "/dev/pts") == 0) + return (0); + + errno = saved_errno; + return (-errno); +} + +/* + * Miscellaneous file-related system calls. + */ + +/* + * fsync() and fdatasync() - On Illumos, these calls translate into a common + * fdsync() syscall with a different parameter. fsync is handled in the + * fsync wrapper. + */ +long +lx_fsync(uintptr_t fd) +{ + int fildes = (int)fd; + struct stat64 statbuf; + + if ((fstat64(fildes, &statbuf) == 0) && + (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode))) + return (-EINVAL); + + return (fsync((int)fd) ? -errno : 0); +} + +long +lx_fdatasync(uintptr_t fd) +{ + int fildes = (int)fd; + struct stat64 statbuf; + + if ((fstat64(fildes, &statbuf) == 0) && + (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode))) + return (-EINVAL); + + return (fdatasync((int)fd) ? -errno : 0); +} + +long +lx_utime(uintptr_t p1, uintptr_t p2) +{ + int ret; + + ret = utime((const char *)p1, (const struct utimbuf *)p2); + + if (ret < 0) { + /* + * If utime() failed and we're in install mode, return success + * if the the reason we failed was because the source file + * didn't actually exist or if we're trying to modify /dev/pts. + */ + if ((lx_install != 0) && + ((errno == ENOENT) || (install_checkpath(p1) == 0))) + return (0); + + return (-errno); + } + + return (0); +} + +long +lx_rmdir(uintptr_t p1) +{ + int r; + char *nm = (char *)p1; + + r = rmdir(nm); + if (r < 0) { + int terr = errno; + + /* + * On both Illumos and Linux rmdir returns EINVAL if the last + * component of the path is '.', but on Illumos we also return + * this errno if we're trying to remove the CWD. Unfortunately, + * at least the LTP test suite assumes that it can rmdir the + * CWD, so we need handle this. We try to get out of the + * directory we're trying to remove. + */ + if (terr == EINVAL) { + int l; + + l = strlen(nm); + if (l >= 2 && !(nm[l - 2] == '/' && nm[l - 1] == '.')) { + if (chdir("..") == 0 && rmdir(nm) == 0) { + return (0); + } + } + } + + return ((terr == EEXIST) ? -ENOTEMPTY : -terr); + } + return (0); +} + +/* + * Exactly the same as Illumos' sysfs(2), except Linux numbers their fs indices + * starting at 0, and Illumos starts at 1. + */ +long +lx_sysfs(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int option = (int)p1; + int res; + + /* + * Linux actually doesn't have #defines for these; their sysfs(2) + * man page literally defines the "option" field as being 1, 2 or 3, + * corresponding to Solaris' GETFSIND, GETFSTYP and GETNFSTYP, + * respectively. + */ + switch (option) { + case 1: + if ((res = sysfs(GETFSIND, (const char *)p2)) < 0) + return (-errno); + + return (res - 1); + + case 2: + if ((res = sysfs(GETFSTYP, (int)p2 + 1, + (char *)p3)) < 0) + return (-errno); + + return (0); + + case 3: + if ((res = sysfs(GETNFSTYP)) < 0) + return (-errno); + + return (res); + + default: + break; + } + + return (-EINVAL); +} + +long +lx_futimesat(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int atfd = (int)p1; + char *path = (char *)p2; + struct timeval *times = (struct timeval *)p3; + + if (atfd == LX_AT_FDCWD) + atfd = AT_FDCWD; + + return (futimesat(atfd, path, times) ? -errno : 0); +} + +/* + * From the utimensat man page: + * On Linux, futimens() is a library function implemented on top of the + * utimensat() system call. To support this, the Linux utimensat() system + * call implements a nonstandard feature: if pathname is NULL, then the + * call modifies the timestamps of the file referred to by the file + * descriptor dirfd (which may refer to any type of file). Using this + * feature, the call futimens(fd, times) is implemented as: + * + * utimensat(fd, NULL, times, 0); + * + * Some of the returns fail here. Linux allows the time to be modified if: + * + * the caller must have write access to the file + * or + * the caller's effective user ID must match the owner of the file + * or + * the caller must have appropriate privileges + * + * We behave differently. We fail with EPERM if: + * + * the calling process's euid has write access to the file but does not match + * the owner of the file and the calling process does not have the + * appropriate privileges + * + * This causes some of the LTP utimensat tests to fail because they expect an + * unprivileged process can update the time on a file it can write but does not + * own. There are also other LTP failures when the test uses attributes + * (e.g. chattr a+) and expects a failure, but we succeed. + */ +long +lx_utimensat(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int fd = (int)p1; + const char *path = (const char *)p2; + const timespec_t *times = (const timespec_t *)p3; + timespec_t ts[2]; + int flag = (int)p4; + + if (times != NULL) { + if (uucopy((void *)p3, ts, sizeof (ts)) == -1) + return (-errno); + + if (ts[0].tv_nsec == LX_UTIME_NOW) + ts[0].tv_nsec = UTIME_NOW; + if (ts[1].tv_nsec == LX_UTIME_NOW) + ts[1].tv_nsec = UTIME_NOW; + + if (ts[0].tv_nsec == LX_UTIME_OMIT) + ts[0].tv_nsec = UTIME_OMIT; + if (ts[1].tv_nsec == LX_UTIME_OMIT) + ts[1].tv_nsec = UTIME_OMIT; + + times = (const timespec_t *)ts; + } + + if (flag == LX_AT_SYMLINK_NOFOLLOW) + flag = AT_SYMLINK_NOFOLLOW; + + if (fd == LX_AT_FDCWD) + fd = AT_FDCWD; + + if (path == NULL) { + return (futimens(fd, times) ? -errno : 0); + } else { + return (utimensat(fd, path, times, flag) ? -errno : 0); + } +} + +/* + * Constructs an absolute path string in buf from the path of fd and the + * relative path string pointed to by "p1". This is required for emulating + * *at() system calls. + * Example: + * If the path of fd is "/foo/bar" and path is "etc" the string returned is + * "/foo/bar/etc", if the fd is a file fd then it fails with ENOTDIR. + * If path is absolute then no modifcations are made to it when copied. + */ +static int +getpathat(int fd, uintptr_t p1, char *outbuf, size_t outbuf_size) +{ + char pathbuf[MAXPATHLEN]; + char fdpathbuf[MAXPATHLEN]; + char *fdpath; + struct stat64 statbuf; + + if (uucopystr((void *)p1, pathbuf, MAXPATHLEN) == -1) + return (-errno); + + /* If the path is absolute then we can early out */ + if ((pathbuf[0] == '/') || (fd == LX_AT_FDCWD)) { + (void) strlcpy(outbuf, pathbuf, outbuf_size); + return (0); + } + + fdpath = lx_fd_to_path(fd, fdpathbuf, sizeof (fdpathbuf)); + if (fdpath == NULL) + return (-EBADF); + + if ((fstat64(fd, &statbuf) < 0)) + return (-EBADF); + + if (!S_ISDIR(statbuf.st_mode)) + return (-ENOTDIR); + + if (snprintf(outbuf, outbuf_size, "%s/%s", fdpath, pathbuf) > + (outbuf_size-1)) + return (-ENAMETOOLONG); + + return (0); +} + +long +lx_mknodat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int atfd = (int)ext1; + char pathbuf[MAXPATHLEN]; + int ret; + + ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf)); + if (ret < 0) + return (ret); + + return (lx_mknod((uintptr_t)pathbuf, p2, p3)); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/fork.c b/usr/src/lib/brand/lx/lx_brand/common/fork.c new file mode 100644 index 0000000000..beb76fcf59 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/fork.c @@ -0,0 +1,186 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. + */ + +#include <errno.h> +#include <unistd.h> +#include <sys/fork.h> +#include <sys/syscall.h> +#include <sys/debug.h> +#include <strings.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +/* + * fork() and vfork() + * + * These cannot be pass thru system calls because we need libc to do its own + * initialization or else bad things will happen (i.e. ending up with a bad + * schedctl page). On Linux, there is no such thing as forkall(), so we use + * fork1() here. + */ + +long +lx_fork(void) +{ + int ret; + + /* + * Inform the in-kernel ptrace(2) subsystem that we are about to + * emulate fork(2). + */ + lx_ptrace_clone_begin(LX_PTRACE_O_TRACEFORK, B_FALSE, 0); + + /* + * Suspend signal delivery, run the stack management prefork handler + * and perform the fork operation. + */ + _sigoff(); + lx_stack_prefork(); + ret = fork1(); + lx_stack_postfork(); + + switch (ret) { + case -1: + _sigon(); + return (-errno); + + case 0: + /* + * Returning in the new child. We must free the stacks and + * thread-specific data objects for the threads we did not + * duplicate; i.e. every other thread. + */ + lx_free_other_stacks(); + + lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_TRUE, 0, + NULL); + + /* + * Re-enable signal delivery in the child and return to the + * new process. + */ + _sigon(); + return (0); + + default: + lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_FALSE, + (ulong_t)ret, NULL); + + /* + * Re-enable signal delivery in the parent and return from + * the emulated system call. + */ + _sigon(); + return (ret); + } +} + +long +lx_vfork(void) +{ + int ret; + lx_sighandlers_t saved; + ucontext_t vforkuc; + ucontext_t *ucp; + lx_tsd_t *lx_tsd = lx_get_tsd(); + + ucp = lx_syscall_regs(); + + /* + * Inform the in-kernel ptrace(2) subsystem that we are about to + * emulate vfork(2). + */ + lx_ptrace_clone_begin(LX_PTRACE_O_TRACEVFORK, B_FALSE, 0); + + /* + * Suspend signal delivery, run the stack management prefork handler + * and perform the vfork operation. We use the same approach as in + * lx_clone for signal handling and child return across vfork. See + * the comments in lx_clone for more detail. + */ + + lx_block_all_signals(); + lx_stack_prefork(); + lx_sighandlers_save(&saved); + lx_tsd->lxtsd_is_vforked++; + ret = vfork(); + if (ret != 0) { + /* parent/error */ + lx_tsd->lxtsd_is_vforked--; + lx_sighandlers_restore(&saved); + } + + switch (ret) { + case -1: + lx_stack_postfork(); + lx_unblock_all_signals(); + return (-errno); + + case 0: + /* + * child + * Unlike the regular fork case where the child also calls + * lx_stack_postfork(), we only do that in the parent once it + * resumes execution. This is required there to wake any other + * threads in that process that are blocked on the lock taken + * in lx_stack_prefork(). + */ + bcopy(ucp, &vforkuc, sizeof (vforkuc)); + vforkuc.uc_brand_data[1] = (caddr_t)vforkuc.uc_brand_data[1] - + LX_NATIVE_STACK_VFORK_GAP; + vforkuc.uc_link = NULL; + + lx_debug("\tvfork native stack sp %p", + vforkuc.uc_brand_data[1]); + + lx_unblock_all_signals(); + + /* Stop for ptrace if required. */ + lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_TRUE, 0, + NULL); + + /* + * Return to the child via the specially constructed vfork(2) + * context. + */ + LX_EMULATE_RETURN(&vforkuc, LX_SYS_vfork, 0, 0); + (void) syscall(SYS_brand, B_EMULATION_DONE, &vforkuc, + LX_SYS_vfork, 0, 0); + + VERIFY(0); + return (0); + + default: + /* parent - child should have exited or exec-ed by now */ + lx_stack_postfork(); + lx_unblock_all_signals(); + lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_FALSE, + (ulong_t)ret, NULL); + return (ret); + } +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c new file mode 100644 index 0000000000..7373a0d02f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c @@ -0,0 +1,1709 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/utsname.h> +#include <sys/inttypes.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/systm.h> +#include <sys/auxv.h> +#include <sys/frame.h> +#include <zone.h> +#include <sys/brand.h> +#include <sys/epoll.h> +#include <sys/stack.h> + +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <errno.h> +#include <syslog.h> +#include <signal.h> +#include <fcntl.h> +#include <synch.h> +#include <libelf.h> +#include <libgen.h> +#include <pthread.h> +#include <utime.h> +#include <dirent.h> +#include <ucontext.h> +#include <libintl.h> +#include <locale.h> + +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> +#include <sys/lx_brand.h> +#include <sys/lx_types.h> +#include <sys/lx_statfs.h> +#include <sys/lx_signal.h> +#include <sys/lx_syscall.h> +#include <sys/lx_thread.h> +#include <lx_auxv.h> +#include <sys/lx_userhz.h> + +/* + * There is a block comment in "uts/common/brand/lx/os/lx_brand.c" that + * describes the functioning of the LX brand in some detail. + * + * *** Setting errno + * + * This emulation library is loaded onto a seperate link map from the + * application whose address space we're running in. The Linux libc errno is + * independent of our native libc errno. To pass back an error the emulation + * function should return -errno back to the Linux caller. + */ + +char lx_release[LX_KERN_RELEASE_MAX]; +char lx_cmd_name[MAXNAMLEN]; +boolean_t lx_no_abort_handler = B_FALSE; + +/* + * Map a linux locale ending string to the solaris equivalent. + */ +struct lx_locale_ending { + const char *linux_end; /* linux ending string */ + const char *solaris_end; /* to transform with this string */ + int le_size; /* linux ending string length */ + int se_size; /* solaris ending string length */ +}; + +#define l2s_locale(lname, sname) \ + {(lname), (sname), sizeof ((lname)) - 1, sizeof ((sname)) - 1} + +#define MAXLOCALENAMELEN 30 +#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ +#endif + +/* + * Most syscalls return an int but some return something else, typically a + * ssize_t. This can be either an int or a long, depending on if we're compiled + * for 32-bit or 64-bit. To correctly propagate the -errno return code in the + * 64-bit case, we declare all emulation wrappers will return a long. Thus, + * when we save the return value into the %eax or %rax register and return to + * Linux, we will have the right size value in both the 32 and 64 bit cases. + */ + +typedef long (*lx_syscall_handler_t)(); + +static lx_syscall_handler_t lx_handlers[LX_NSYSCALLS + 1]; + +static uintptr_t stack_size; + +#if defined(_LP64) +long lx_fsb; +long lx_fs; +#endif +int lx_install = 0; /* install mode enabled if non-zero */ +int lx_verbose = 0; /* verbose mode enabled if non-zero */ +int lx_debug_enabled = 0; /* debugging output enabled if non-zero */ + +pid_t zoneinit_pid; /* zone init PID */ + +thread_key_t lx_tsd_key; + +uint_t lx_hz_scale; /* USER_HZ scaling factor */ + +int +uucopy_unsafe(const void *src, void *dst, size_t n) +{ + bcopy(src, dst, n); + return (0); +} + +int +uucopystr_unsafe(const void *src, void *dst, size_t n) +{ + (void) strncpy((char *)src, dst, n); + return (0); +} + +static void +i_lx_msg(int fd, char *msg, va_list ap) +{ + int i; + char buf[LX_MSG_MAXLEN]; + + /* LINTED [possible expansion issues] */ + i = vsnprintf(buf, sizeof (buf), msg, ap); + buf[LX_MSG_MAXLEN - 1] = '\0'; + if (i == -1) + return; + + /* if debugging is enabled, send this message to debug output */ + if (LX_DEBUG_ISENABLED) + lx_debug(buf); + + if (fd == 2) { + /* + * We let the user choose whether or not to see these + * messages on the console. + */ + if (lx_verbose == 0) + return; + } + + /* we retry in case of EINTR */ + do { + i = write(fd, buf, strlen(buf)); + } while ((i == -1) && (errno == EINTR)); +} + +/*PRINTFLIKE1*/ +void +lx_err(char *msg, ...) +{ + va_list ap; + + assert(msg != NULL); + + va_start(ap, msg); + i_lx_msg(STDERR_FILENO, msg, ap); + va_end(ap); +} + +/* + * This is just a non-zero exit value which also isn't one that would allow + * us to easily detect if a branded process exited because of a recursive + * fatal error. + */ +#define LX_ERR_FATAL 42 + +/* + * Our own custom version of abort(), this routine will be used in place + * of the one located in libc. The primary difference is that this version + * will first reset the signal handler for SIGABRT to SIG_DFL, ensuring the + * SIGABRT sent causes us to dump core and is not caught by a user program. + */ +void +abort(void) +{ + static int aborting = 0; + + struct sigaction sa; + sigset_t sigmask; + + /* watch out for recursive calls to this function */ + if (aborting != 0) + exit(LX_ERR_FATAL); + + aborting = 1; + + /* + * Block all signals here to avoid taking any signals while exiting + * in an effort to avoid any strange user interaction with our death. + */ + (void) sigfillset(&sigmask); + (void) sigprocmask(SIG_BLOCK, &sigmask, NULL); + + /* + * Our own version of abort(3C) that we know will never call + * a user-installed SIGABRT handler first. We WANT to die. + * + * Do this by resetting the handler to SIG_DFL, and releasing any + * held SIGABRTs. + * + * If no SIGABRTs are pending, send ourselves one. + * + * The while loop is a bit of overkill, but abort(3C) does it to + * assure it never returns so we will as well. + */ + (void) sigemptyset(&sa.sa_mask); + sa.sa_sigaction = SIG_DFL; + sa.sa_flags = 0; + + for (;;) { + (void) sigaction(SIGABRT, &sa, NULL); + (void) sigrelse(SIGABRT); + (void) thr_kill(thr_self(), SIGABRT); + } + + /*NOTREACHED*/ +} + +/*PRINTFLIKE1*/ +void +lx_msg(char *msg, ...) +{ + va_list ap; + + assert(msg != NULL); + va_start(ap, msg); + i_lx_msg(STDOUT_FILENO, msg, ap); + va_end(ap); +} + +/*PRINTFLIKE1*/ +void +lx_err_fatal(char *msg, ...) +{ + va_list ap; + + assert(msg != NULL); + + va_start(ap, msg); + i_lx_msg(STDERR_FILENO, msg, ap); + va_end(ap); + abort(); +} + +/* + * See if it is safe to alloca() sz bytes. Return 1 for yes, 0 for no. + * We can't be certain we won't blow the stack since we don't know where it + * starts, but since the stack is only two pages we know any allocation bigger + * than that will blow the stack. Fortunately most allocations are small (e.g. + * 128 bytes). + */ +int +lx_check_alloca(size_t sz) +{ + uintptr_t sp = (uintptr_t)&sz; + uintptr_t end = sp - sz; + + return ((end < sp) && (sz < stack_size)); +} + +/*PRINTFLIKE1*/ +void +lx_unsupported(char *msg, ...) +{ + va_list ap; + char dmsg[256]; + int lastc; + + assert(msg != NULL); + + /* make a brand call so we can easily dtrace unsupported actions */ + va_start(ap, msg); + (void) vsnprintf(dmsg, sizeof (dmsg), msg, ap); + dmsg[255] = '\0'; + lastc = strlen(dmsg) - 1; + if (dmsg[lastc] == '\n') + dmsg[lastc] = '\0'; + (void) syscall(SYS_brand, B_UNSUPPORTED, dmsg); + va_end(ap); + + /* send the msg to the error stream */ + va_start(ap, msg); + i_lx_msg(STDERR_FILENO, msg, ap); + va_end(ap); +} + +int lx_init(int argc, char *argv[], char *envp[]); + +lx_tsd_t * +lx_get_tsd(void) +{ + int ret; + lx_tsd_t *lx_tsd; + + if ((ret = thr_getspecific(lx_tsd_key, (void **)&lx_tsd)) != 0) { + lx_err_fatal("lx_get_tsd: unable to read " + "thread-specific data: %s", strerror(ret)); + } + + assert(lx_tsd != 0); + + return (lx_tsd); +} + +/* + * This function is called from the kernel like a signal handler. Each + * function call is a request to provide emulation for a system call that, on + * illumos, is implemented in userland. The system call number selection and + * argument parsing have already been done by the kernel. + */ +void +lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args) +{ + long emu_ret; + int emu_errno = 0; + + LX_EMULATE_ENTER(ucp, syscall_num, args); + lx_debug("lx_emulate(%p, %d, [%p, %p, %p, %p, %p, %p])\n", + ucp, syscall_num, args[0], args[1], args[2], args[3], args[4], + args[5]); + + /* + * The kernel should have saved us a context that will not restore the + * previous signal mask. Some emulated system calls alter the signal + * mask; restoring it after the emulation would cancel that out. + */ + assert(!(ucp->uc_flags & UC_SIGMASK)); + + /* + * The kernel ensures that the syscall_num is sane; Use it as is. + */ + assert(syscall_num >= 0); + assert(syscall_num < (sizeof (lx_handlers) / sizeof (lx_handlers[0]))); + if (lx_handlers[syscall_num] == NULL) { + lx_err_fatal("lx_emulate: kernel sent us a call we cannot " + "emulate (%d)", syscall_num); + } + + /* + * Call our handler function: + */ + emu_ret = lx_handlers[syscall_num](args[0], args[1], args[2], args[3], + args[4], args[5]); + + /* + * If the return value is between -1 and -4095 then it's an errno. + * The kernel will translate it to the Linux equivalent for us. + */ + if (emu_ret < 0 && emu_ret > -4096) { + emu_errno = (int)-emu_ret; + } + + /* + * Return to the context we were passed + */ + LX_EMULATE_RETURN(ucp, syscall_num, emu_ret, emu_errno); + lx_debug("\tlx_emulate(%d) done (ret %ld / 0x%p ; errno %d)", + syscall_num, emu_ret, emu_ret, emu_errno); + (void) syscall(SYS_brand, B_EMULATION_DONE, ucp, syscall_num, emu_ret, + emu_errno); + + assert(!"cannot be returned here"); +} + +static void +lx_close_fh(FILE *file) +{ + int fd, fd_new; + + if (file == NULL) + return; + + if ((fd = fileno(file)) < 0) + return; + + fd_new = dup(fd); + if (fd_new == -1) + return; + + (void) fclose(file); + (void) dup2(fd_new, fd); + (void) close(fd_new); +} + + +extern int set_l10n_alternate_root(char *path); + +/* + * Initialize the thread specific data for this thread. + */ +void +lx_init_tsd(lx_tsd_t *lxtsd) +{ + int err; + + bzero(lxtsd, sizeof (*lxtsd)); + lxtsd->lxtsd_exit = LX_ET_NONE; + + /* + * The Linux alternate signal stack is initially disabled: + */ + lxtsd->lxtsd_sigaltstack.ss_flags = LX_SS_DISABLE; + + /* + * Create a per-thread exit context from the current register and + * native/brand stack state. Replace the saved program counter value + * with the address of lx_exit_common(); we wish to revector there when + * the thread or process is exiting. + */ + if (getcontext(&lxtsd->lxtsd_exit_context) != 0) { + lx_err_fatal("Unable to initialize thread-specific exit " + "context: %s", strerror(errno)); + } + LX_REG(&lxtsd->lxtsd_exit_context, REG_PC) = (uintptr_t)lx_exit_common; + + /* + * Align the stack pointer and clear the frame pointer. + */ + LX_REG(&lxtsd->lxtsd_exit_context, REG_FP) = 0; + LX_REG(&lxtsd->lxtsd_exit_context, REG_SP) &= ~(STACK_ALIGN - 1UL); +#if defined(_LP64) +#if (STACK_ENTRY_ALIGN != 8) && (STACK_ALIGN != 16) +#error "lx_init_tsd: unexpected STACK_[ENTRY_]ALIGN values" +#endif + /* + * The AMD64 ABI requires that, on entry to a function, the stack + * pointer must be 8-byte aligned, but _not_ 16-byte aligned. When + * the frame pointer is pushed, the alignment will then be correct. + */ + LX_REG(&lxtsd->lxtsd_exit_context, REG_SP) -= STACK_ENTRY_ALIGN; +#endif + + /* + * Block all signals in the exit context to avoid taking any signals + * (to the degree possible) while exiting. + */ + (void) sigfillset(&lxtsd->lxtsd_exit_context.uc_sigmask); + + if ((err = thr_setspecific(lx_tsd_key, lxtsd)) != 0) { + lx_err_fatal("Unable to initialize thread-specific data: %s", + strerror(err)); + } +} + +void +lx_jump_to_linux(ucontext_t *ucp) +{ + extern void setcontext_sigmask(ucontext_t *); + + /* + * Call into this private libc interface to allow us to use only the + * signal mask handling part of a regular setcontext() operation. + */ + setcontext_sigmask(ucp); + + if (syscall(SYS_brand, B_JUMP_TO_LINUX, ucp) != 0) { + lx_err_fatal("B_JUMP_TO_LINUX failed: %s", strerror(errno)); + } + + /* + * This system call should not return. + */ + abort(); +} + +static void +lx_start(uintptr_t sp, uintptr_t entry) +{ + ucontext_t jump_uc; + + if (getcontext(&jump_uc) != 0) { + lx_err_fatal("Unable to getcontext for program start: %s", + strerror(errno)); + } + + /* + * We want to load the general registers from this + * context, and switch to the BRAND stack. + */ + jump_uc.uc_flags = UC_CPU; + jump_uc.uc_brand_data[0] = (void *)LX_UC_STACK_BRAND; + + LX_REG(&jump_uc, REG_FP) = (uintptr_t)NULL; + LX_REG(&jump_uc, REG_SP) = sp; + LX_REG(&jump_uc, REG_PC) = entry; + + /* + * The AMD64 ABI states that at process entry, %rdx contains "a + * function pointer that the application should register with + * atexit()". This behavior has been observed in statically linked + * i386 programs as well. As a precaution, all of the registers are + * zeroed prior to initial execution. + */ +#if defined(_LP64) + LX_REG(&jump_uc, REG_RAX) = 0; + LX_REG(&jump_uc, REG_RCX) = 0; + LX_REG(&jump_uc, REG_RDX) = 0; + LX_REG(&jump_uc, REG_RBX) = 0; + LX_REG(&jump_uc, REG_RBP) = 0; + LX_REG(&jump_uc, REG_RSI) = 0; + LX_REG(&jump_uc, REG_RDI) = 0; + LX_REG(&jump_uc, REG_R8) = 0; + LX_REG(&jump_uc, REG_R9) = 0; + LX_REG(&jump_uc, REG_R10) = 0; + LX_REG(&jump_uc, REG_R11) = 0; + LX_REG(&jump_uc, REG_R12) = 0; + LX_REG(&jump_uc, REG_R13) = 0; + LX_REG(&jump_uc, REG_R14) = 0; + LX_REG(&jump_uc, REG_R15) = 0; +#else + LX_REG(&jump_uc, EAX) = 0; + LX_REG(&jump_uc, ECX) = 0; + LX_REG(&jump_uc, EDX) = 0; + LX_REG(&jump_uc, EBX) = 0; + LX_REG(&jump_uc, EBP) = 0; + LX_REG(&jump_uc, ESI) = 0; + LX_REG(&jump_uc, EDI) = 0; +#endif /* defined(_LP64) */ + + lx_debug("starting Linux program sp %p ldentry %p", sp, entry); + lx_jump_to_linux(&jump_uc); +} + +enum lx_env_setting { + LXES_INSTALL = 0, + LXES_VERBOSE, + LXES_DTRACE, + LXES_DEBUG, + LXES_DEBUG_FILE, + LXES_NO_ABORT_HANDLER, + LXES_RELEASE, + LXES_VERSION, + LXES_STRICT, + LXES_LIMIT +}; + +static void +lx_parse_env(char *envp[], char *settings[]) +{ + int i, j; + char *env; + + typedef struct lx_env_entry { + char *lee_name; + int lee_len; + int lee_index; + } lx_env_entry_t; +#define LX_ENV_ENTRY(name, idx) { name, (sizeof (name)) - 1, idx } + static const lx_env_entry_t lx_env_entries[] = { + LX_ENV_ENTRY("LX_INSTALL", LXES_INSTALL), + LX_ENV_ENTRY("LX_VERBOSE", LXES_VERBOSE), + LX_ENV_ENTRY("LX_DTRACE", LXES_DTRACE), + LX_ENV_ENTRY("LX_DEBUG", LXES_DEBUG), + LX_ENV_ENTRY("LX_DEBUG_FILE", LXES_DEBUG_FILE), + LX_ENV_ENTRY("LX_NO_ABORT_HANDLER", LXES_NO_ABORT_HANDLER), + LX_ENV_ENTRY("LX_RELEASE", LXES_RELEASE), + LX_ENV_ENTRY("LX_VERSION", LXES_VERSION), + LX_ENV_ENTRY("LX_STRICT", LXES_STRICT) + }; +#define LX_ENV_ENTRY_COUNT \ + (sizeof (lx_env_entries) / sizeof (lx_env_entries[0])) + + for (i = 0; (env = envp[i]) != NULL; i++) { + if (env[0] != 'L' || env[1] != 'X' || env[2] != '_') + continue; + for (j = 0; j < LX_ENV_ENTRY_COUNT; j++) { + const lx_env_entry_t *lee = &lx_env_entries[j]; + + if (strncmp(env, lee->lee_name, lee->lee_len) != 0 || + env[lee->lee_len] != '=') + continue; + settings[lee->lee_index] = &env[lee->lee_len + 1]; + break; + } + } +} + +/*ARGSUSED*/ +int +lx_init(int argc, char *argv[], char *envp[]) +{ + auxv_t *ap, *oap; + long *p; + int err; + lx_elf_data_t edp; + lx_brand_registration_t reg; + lx_tsd_t *lxtsd; + char *lx_settings[LXES_LIMIT]; + + bzero(®, sizeof (reg)); + stack_size = 2 * sysconf(_SC_PAGESIZE); + + lx_hz_scale = sysconf(_SC_CLK_TCK) / LX_USERHZ; + + /* + * We need to shutdown all libc stdio. libc stdio normally goes to + * file descriptors, but since we're actually part of a linux + * process we don't own these file descriptors and we can't make + * any assumptions about their state. + */ + lx_close_fh(stdin); + lx_close_fh(stdout); + lx_close_fh(stderr); + + /* + * Parse LX-related settings out of the environment array. + * This is done manually instead of utilizing libc's getenv() to avoid + * triggering any env-cleaning routines which are present. + */ + bzero(lx_settings, sizeof (lx_settings)); + lx_parse_env(envp, lx_settings); + + /* + * Setting LX_NO_ABORT_HANDLER in the environment will prevent the + * emulated Linux program from modifying the signal handling + * disposition for SIGSEGV or SIGABRT. It is useful for debugging + * programs which fall over themselves to prevent useful core files + * being generated. + */ + lx_no_abort_handler = (lx_settings[LXES_NO_ABORT_HANDLER] != NULL); + + lx_debug_init(lx_settings[LXES_DTRACE] != NULL, + lx_settings[LXES_DEBUG] != NULL, + lx_settings[LXES_DEBUG_FILE]); + + if (lx_settings[LXES_RELEASE] == NULL) { + if (zone_getattr(getzoneid(), LX_ATTR_KERN_RELEASE, + lx_release, sizeof (lx_release)) <= 0) + (void) strlcpy(lx_release, "2.4.21", + LX_KERN_RELEASE_MAX); + } else { + (void) strlcpy(lx_release, lx_settings[LXES_RELEASE], + LX_KERN_RELEASE_MAX); + } + + if (lx_settings[LXES_RELEASE] != NULL || + lx_settings[LXES_VERSION] != NULL) { + if (syscall(SYS_brand, B_OVERRIDE_KERN_VER, + lx_settings[LXES_RELEASE], + lx_settings[LXES_VERSION]) != 0) { + lx_debug("failed to override kernel release/version"); + } + } + lx_debug("lx_release: %s\n", lx_release); + + + /* + * Should we kill an application that attempts an unimplemented + * system call? + */ + if (lx_settings[LXES_STRICT] != NULL) { + reg.lxbr_flags |= LX_PROC_STRICT_MODE; + lx_debug("STRICT mode enabled.\n"); + } + + /* + * Are we in install mode? + */ + if (lx_settings[LXES_INSTALL] != NULL) { + reg.lxbr_flags |= LX_PROC_INSTALL_MODE; + lx_install = 1; + lx_debug("INSTALL mode enabled.\n"); + } + + (void) strlcpy(lx_cmd_name, basename(argv[0]), sizeof (lx_cmd_name)); + lx_debug("executing linux process: %s", argv[0]); + lx_debug("branding myself and setting handler to 0x%p", + (void *)lx_emulate); + + reg.lxbr_version = LX_VERSION; + reg.lxbr_handler = (void *)&lx_emulate; + + /* + * Register the address of the user-space handler with the lx brand + * module. As a side-effect this leaves the thread in native syscall + * mode so that it's ok to continue to make syscalls during setup. We + * need to switch to Linux mode at the end of initialization. + */ + if (syscall(SYS_brand, B_REGISTER, ®)) + lx_err_fatal("failed to brand the process"); + + /* Look up the PID that serves as init for this zone */ + if ((err = lx_lpid_to_spid(1, &zoneinit_pid)) < 0) + lx_err_fatal("Unable to find PID for zone init process: %s", + strerror(err)); + + /* + * Upload data about the lx executable from the kernel. + */ + if (syscall(SYS_brand, B_ELFDATA, (void *)&edp)) + lx_err_fatal("failed to get required ELF data from the kernel"); + + if (lx_statfs_init() != 0) + lx_err_fatal("failed to setup the statfs translator"); + + /* + * Find the aux vector on the stack. + */ + p = (long *)envp; + while (*p != (uintptr_t)NULL) + p++; + + /* + * Now 'p' points at the NULL word immediately following the environ + * pointers. The list of auxv entries _should_ immediately follow. + * If anything (such as the native linker or libc) has removed entries + * from the environment array, extra NULLs will be present. + * + * The brand library takes care to avoid such behavior (via the + * lx_parse_env routine above) but a belt-and-suspenders approach is + * taken for safety. + * + * The address following the NULL spacer is recorded as the target for + * auxv translation and any addition NULLs following it are skipped + * until the first auxv entry is located. + */ + p++; + oap = (auxv_t *)p; + while (*p == (uintptr_t)NULL) + p++; + ap = (auxv_t *)p; + + /* + * Translate auxv entries to Linux equivalents. + */ + for (; ap->a_type != AT_NULL; ap++) { + if (lx_auxv_stol(ap, oap, &edp) == 0) { + /* + * Copy only auxv entries which Linux programs will + * understand. Other entries will be skipped. + */ + oap++; + } + } + + /* NULL out skipped entries */ + if (oap < ap) { + bzero(oap, (uintptr_t)ap - (uintptr_t)oap); + } + + /* Setup signal handler information. */ + if (lx_siginit()) { + lx_err_fatal("failed to initialize lx signals for the " + "branded process"); + } + + /* Setup thread-specific data area for managing linux threads. */ + if ((err = thr_keycreate(&lx_tsd_key, NULL)) != 0) { + lx_err_fatal("thr_keycreate(lx_tsd_key) failed: %s", + strerror(err)); + } + + lx_debug("thr_keycreate created lx_tsd_key (%d)", lx_tsd_key); + + /* + * Initialize the thread specific data for this thread. + */ + if ((lxtsd = malloc(sizeof (*lxtsd))) == NULL) { + lx_err_fatal("failed to allocate tsd for main thread: %s", + strerror(errno)); + } + lx_debug("lx tsd allocated @ %p", lxtsd); + lx_init_tsd(lxtsd); + + /* + * Allocate the brand emulation stack for the main process thread. + * Register the thread-specific data structure with the stack list so + * that it may be freed at thread exit or fork(2). + */ + lx_install_stack(NULL, 0, lxtsd); + + /* + * The brand linker expects the stack pointer to point to + * "argc", which is just before &argv[0]. + */ + lx_start((uintptr_t)argv - sizeof (void *), edp.ed_ldentry); + + /*NOTREACHED*/ + abort(); + return (0); +} + +/* + * We "return" to this function via a context hand-crafted by + * "lx_init_tsd()"; see that function for more detail. + * + * NOTE: Our call frame is on the main thread stack, not the alternate native + * stack -- it is safe to release the latter here. The frame does not have a + * valid return address, so this function MUST NOT return. + */ +void +lx_exit_common(void) +{ + lx_tsd_t *lxtsd = lx_get_tsd(); + int ev = (0xff & lxtsd->lxtsd_exit_status); + + switch (lxtsd->lxtsd_exit) { + case LX_ET_EXIT: + lx_debug("lx_exit_common(LX_ET_EXIT, %d, %d)\n", thr_self(), + ev); + + if (thr_self() == 1) { + /* + * Modern versions of glibc will call the exit_group + * syscall when exit(3) is called, but if the primary + * thread explicitly invokes the exit syscall we now + * need to exit with the proper value. + */ + exit(ev); + } else { + /* + * If the thread is exiting, but not the entire process, + * we must free the stack we allocated for usermode + * emulation. This is safe to do here because the + * setcontext() put us back on the BRAND stack for this + * process. This function also frees the + * thread-specific data object for this thread. + */ + lx_free_stack(); + + /* + * The native thread return value is never seen so we + * pass NULL. + */ + thr_exit(NULL); + } + break; + + case LX_ET_EXIT_GROUP: + lx_debug("lx_exit_common(LX_ET_EXIT_GROUP, %d)\n", ev); + exit(ev); + break; + + default: + abort(); + } + + abort(); +} + +const ucontext_t * +lx_find_brand_uc(void) +{ + ucontext_t *ucp = NULL; + + /* + * Ask for the current emulation (or signal handling) ucontext_t... + */ + assert(syscall(SYS_brand, B_GET_CURRENT_CONTEXT, &ucp) == 0); + + for (;;) { + uintptr_t flags; + + lx_debug("lx_find_brand_uc: inspect ucp %p...\n", ucp); + assert(ucp != NULL); + + flags = (uintptr_t)ucp->uc_brand_data[0]; + + if (flags & LX_UC_STACK_BRAND) { + lx_debug("lx_find_brand_uc: ucp %p\n", ucp); + + return (ucp); + } + + lx_debug("lx_find_brand_uc: skip non-BRAND ucp %p\n", ucp); + + /* + * Walk up the context chain to find the most recently stored + * brand register state. + */ + ucp = ucp->uc_link; + } +} + +uintptr_t +lx_find_brand_sp(void) +{ + const ucontext_t *ucp = lx_find_brand_uc(); + uintptr_t sp = LX_REG(ucp, REG_SP); + + lx_debug("lx_find_brand_sp: ucp %p sp %p\n", ucp, sp); + + return (sp); +} + +ucontext_t * +lx_syscall_regs(void) +{ + ucontext_t *ucp = NULL; + uintptr_t flags; + + /* + * Ask for the current emulation (or signal handling) ucontext_t... + */ + assert(syscall(SYS_brand, B_GET_CURRENT_CONTEXT, &ucp) == 0); + assert(ucp != NULL); + + /* + * Use of the lx_syscall_regs() function implies that the topmost (i.e. + * current) context is for a system call emulation request from the + * kernel, rather than a signal handling frame. + */ + flags = (uintptr_t)ucp->uc_brand_data[0]; + assert(flags & LX_UC_FRAME_IS_SYSCALL); + + lx_debug("lx_syscall_regs: ucp %p\n", ucp); + + return (ucp); +} + +int +lx_lpid_to_spair(pid_t lpid, pid_t *spid, lwpid_t *slwp) +{ + pid_t pid; + lwpid_t tid; + + if (lpid == 0) { + pid = getpid(); + tid = thr_self(); + } else { + if (syscall(SYS_brand, B_LPID_TO_SPAIR, lpid, &pid, &tid) < 0) + return (-errno); + + /* + * If the returned pid is -1, that indicates we tried to + * look up the PID for init, but that process no longer + * exists. + */ + if (pid == -1) + return (-ESRCH); + } + + if (uucopy(&pid, spid, sizeof (pid_t)) != 0) + return (-errno); + + if (uucopy(&tid, slwp, sizeof (lwpid_t)) != 0) + return (-errno); + + return (0); +} + +int +lx_lpid_to_spid(pid_t lpid, pid_t *spid) +{ + lwpid_t slwp; + + return (lx_lpid_to_spair(lpid, spid, &slwp)); +} + +char * +lx_fd_to_path(int fd, char *buf, int buf_size) +{ + char path_proc[MAXPATHLEN]; + pid_t pid; + int n; + + assert((buf != NULL) && (buf_size >= 0)); + + if (fd < 0) + return (NULL); + + if ((pid = getpid()) == -1) + return (NULL); + + (void) snprintf(path_proc, MAXPATHLEN, + "/native/proc/%d/path/%d", pid, fd); + + if ((n = readlink(path_proc, buf, buf_size - 1)) == -1) + return (NULL); + buf[n] = '\0'; + + return (buf); +} + +#if defined(_LP64) +/* The following is the 64-bit syscall table */ + +static lx_syscall_handler_t lx_handlers[] = { + NULL, /* 0: read */ + NULL, /* 1: write */ + NULL, /* 2: open */ + NULL, /* 3: close */ + NULL, /* 4: stat */ + NULL, /* 5: fstat */ + NULL, /* 6: lstat */ + NULL, /* 7: poll */ + NULL, /* 8: lseek */ + NULL, /* 9: mmap */ + NULL, /* 10: mprotect */ + NULL, /* 11: munmap */ + NULL, /* 12: brk */ + lx_rt_sigaction, /* 13: rt_sigaction */ + lx_rt_sigprocmask, /* 14: rt_sigprocmask */ + lx_rt_sigreturn, /* 15: rt_sigreturn */ + NULL, /* 16: ioctl */ + NULL, /* 17: pread64 */ + NULL, /* 18: pwrite64 */ + NULL, /* 19: readv */ + NULL, /* 20: writev */ + NULL, /* 21: access */ + NULL, /* 22: pipe */ + NULL, /* 23: select */ + NULL, /* 24: sched_yield */ + NULL, /* 25: mremap */ + NULL, /* 26: msync */ + NULL, /* 27: mincore */ + NULL, /* 28: madvise */ + lx_shmget, /* 29: shmget */ + lx_shmat, /* 30: shmat */ + lx_shmctl, /* 31: shmctl */ + NULL, /* 32: dup */ + NULL, /* 33: dup2 */ + NULL, /* 34: pause */ + NULL, /* 35: nanosleep */ + NULL, /* 36: getitimer */ + NULL, /* 37: alarm */ + lx_setitimer, /* 38: setitimer */ + NULL, /* 39: getpid */ + lx_sendfile64, /* 40: sendfile */ + NULL, /* 41: socket */ + NULL, /* 42: connect */ + NULL, /* 43: accept */ + NULL, /* 44: sendto */ + NULL, /* 45: recvfrom */ + NULL, /* 46: sendmsg */ + NULL, /* 47: recvmsg */ + NULL, /* 48: shutdown */ + NULL, /* 49: bind */ + NULL, /* 50: listen */ + NULL, /* 51: getsockname */ + NULL, /* 52: getpeername */ + NULL, /* 53: socketpair */ + NULL, /* 54: setsockopt */ + NULL, /* 55: getsockopt */ + lx_clone, /* 56: clone */ + lx_fork, /* 57: fork */ + lx_vfork, /* 58: vfork */ + lx_execve, /* 59: execve */ + lx_exit, /* 60: exit */ + NULL, /* 61: wait4 */ + NULL, /* 62: kill */ + NULL, /* 63: uname */ + lx_semget, /* 64: semget */ + lx_semop, /* 65: semop */ + lx_semctl, /* 66: semctl */ + lx_shmdt, /* 67: shmdt */ + lx_msgget, /* 68: msgget */ + lx_msgsnd, /* 69: msgsnd */ + lx_msgrcv, /* 70: msgrcv */ + lx_msgctl, /* 71: msgctl */ + NULL, /* 72: fcntl */ + NULL, /* 73: flock */ + lx_fsync, /* 74: fsync */ + lx_fdatasync, /* 75: fdatasync */ + lx_truncate, /* 76: truncate */ + lx_ftruncate, /* 77: ftruncate */ + NULL, /* 78: getdents */ + NULL, /* 79: getcwd */ + NULL, /* 80: chdir */ + NULL, /* 81: fchdir */ + NULL, /* 82: rename */ + NULL, /* 83: mkdir */ + lx_rmdir, /* 84: rmdir */ + NULL, /* 85: creat */ + NULL, /* 86: link */ + NULL, /* 87: unlink */ + NULL, /* 88: symlink */ + NULL, /* 89: readlink */ + NULL, /* 90: chmod */ + NULL, /* 91: fchmod */ + NULL, /* 92: chown */ + NULL, /* 93: fchown */ + NULL, /* 94: lchown */ + NULL, /* 95: umask */ + NULL, /* 96: gettimeofday */ + NULL, /* 97: getrlimit */ + NULL, /* 98: getrusage */ + NULL, /* 99: sysinfo */ + NULL, /* 100: times */ + NULL, /* 101: ptrace */ + NULL, /* 102: getuid */ + NULL, /* 103: syslog */ + NULL, /* 104: getgid */ + NULL, /* 105: setuid */ + NULL, /* 106: setgid */ + NULL, /* 107: geteuid */ + NULL, /* 108: getegid */ + NULL, /* 109: setpgid */ + NULL, /* 110: getppid */ + NULL, /* 111: getpgrp */ + NULL, /* 112: setsid */ + NULL, /* 113: setreuid */ + NULL, /* 114: setregid */ + lx_getgroups, /* 115: getgroups */ + lx_setgroups, /* 116: setgroups */ + NULL, /* 117: setresuid */ + NULL, /* 118: getresuid */ + NULL, /* 119: setresgid */ + NULL, /* 120: getresgid */ + NULL, /* 121: getpgid */ + NULL, /* 122: setfsuid */ + NULL, /* 123: setfsgid */ + NULL, /* 124: getsid */ + lx_capget, /* 125: capget */ + lx_capset, /* 126: capset */ + lx_rt_sigpending, /* 127: rt_sigpending */ + lx_rt_sigtimedwait, /* 128: rt_sigtimedwait */ + lx_rt_sigqueueinfo, /* 129: rt_sigqueueinfo */ + lx_rt_sigsuspend, /* 130: rt_sigsuspend */ + lx_sigaltstack, /* 131: sigaltstack */ + lx_utime, /* 132: utime */ + lx_mknod, /* 133: mknod */ + NULL, /* 134: uselib */ + NULL, /* 135: personality */ + NULL, /* 136: ustat */ + lx_statfs, /* 137: statfs */ + lx_fstatfs, /* 138: fstatfs */ + lx_sysfs, /* 139: sysfs */ + NULL, /* 140: getpriority */ + NULL, /* 141: setpriority */ + NULL, /* 142: sched_setparam */ + NULL, /* 143: sched_getparam */ + NULL, /* 144: sched_setscheduler */ + NULL, /* 145: sched_getscheduler */ + NULL, /* 146: sched_get_priority_max */ + NULL, /* 147: sched_get_priority_min */ + NULL, /* 148: sched_rr_get_interval */ + NULL, /* 149: mlock */ + NULL, /* 150: munlock */ + NULL, /* 151: mlockall */ + NULL, /* 152: munlockall */ + NULL, /* 153: vhangup */ + NULL, /* 154: modify_ldt */ + NULL, /* 155: pivot_root */ + lx_sysctl, /* 156: sysctl */ + NULL, /* 157: prctl */ + NULL, /* 158: arch_prctl */ + lx_adjtimex, /* 159: adjtimex */ + NULL, /* 160: setrlimit */ + NULL, /* 161: chroot */ + NULL, /* 162: sync */ + NULL, /* 163: acct */ + lx_settimeofday, /* 164: settimeofday */ + lx_mount, /* 165: mount */ + NULL, /* 166: umount2 */ + NULL, /* 167: swapon */ + NULL, /* 168: swapoff */ + NULL, /* 169: reboot */ + NULL, /* 170: sethostname */ + NULL, /* 171: setdomainname */ + NULL, /* 172: iopl */ + NULL, /* 173: ioperm */ + NULL, /* 174: create_module */ + NULL, /* 175: init_module */ + NULL, /* 176: delete_module */ + NULL, /* 177: get_kernel_syms */ + lx_query_module, /* 178: query_module */ + NULL, /* 179: quotactl */ + NULL, /* 180: nfsservctl */ + NULL, /* 181: getpmsg */ + NULL, /* 182: putpmsg */ + NULL, /* 183: afs_syscall */ + NULL, /* 184: tux */ + NULL, /* 185: security */ + NULL, /* 186: gettid */ + NULL, /* 187: readahead */ + NULL, /* 188: setxattr */ + NULL, /* 189: lsetxattr */ + NULL, /* 190: fsetxattr */ + NULL, /* 191: getxattr */ + NULL, /* 192: lgetxattr */ + NULL, /* 193: fgetxattr */ + NULL, /* 194: listxattr */ + NULL, /* 195: llistxattr */ + NULL, /* 196: flistxattr */ + NULL, /* 197: removexattr */ + NULL, /* 198: lremovexattr */ + NULL, /* 199: fremovexattr */ + NULL, /* 200: tkill */ + NULL, /* 201: time */ + NULL, /* 202: futex */ + NULL, /* 203: sched_setaffinity */ + NULL, /* 204: sched_getaffinity */ + NULL, /* 205: set_thread_area */ + NULL, /* 206: io_setup */ + NULL, /* 207: io_destroy */ + NULL, /* 208: io_getevents */ + NULL, /* 209: io_submit */ + NULL, /* 210: io_cancel */ + NULL, /* 211: get_thread_area */ + NULL, /* 212: lookup_dcookie */ + NULL, /* 213: epoll_create */ + NULL, /* 214: epoll_ctl_old */ + NULL, /* 215: epoll_wait_old */ + NULL, /* 216: remap_file_pages */ + NULL, /* 217: getdents64 */ + NULL, /* 218: set_tid_address */ + NULL, /* 219: restart_syscall */ + lx_semtimedop, /* 220: semtimedop */ + NULL, /* 221: fadvise64 */ + NULL, /* 222: timer_create */ + lx_timer_settime, /* 223: timer_settime */ + lx_timer_gettime, /* 224: timer_gettime */ + lx_timer_getoverrun, /* 225: timer_getoverrun */ + lx_timer_delete, /* 226: timer_delete */ + NULL, /* 227: clock_settime */ + NULL, /* 228: clock_gettime */ + NULL, /* 229: clock_getres */ + lx_clock_nanosleep, /* 230: clock_nanosleep */ + lx_group_exit, /* 231: exit_group */ + NULL, /* 232: epoll_wait */ + NULL, /* 233: epoll_ctl */ + NULL, /* 234: tgkill */ + lx_utimes, /* 235: utimes */ + NULL, /* 236: vserver */ + NULL, /* 237: mbind */ + NULL, /* 238: set_mempolicy */ + NULL, /* 239: get_mempolicy */ + NULL, /* 240: mq_open */ + NULL, /* 241: mq_unlink */ + NULL, /* 242: mq_timedsend */ + NULL, /* 243: mq_timedreceive */ + NULL, /* 244: mq_notify */ + NULL, /* 245: mq_getsetattr */ + NULL, /* 246: kexec_load */ + NULL, /* 247: waitid */ + NULL, /* 248: add_key */ + NULL, /* 249: request_key */ + NULL, /* 250: keyctl */ + NULL, /* 251: ioprio_set */ + NULL, /* 252: ioprio_get */ + lx_inotify_init, /* 253: inotify_init */ + lx_inotify_add_watch, /* 254: inotify_add_watch */ + lx_inotify_rm_watch, /* 255: inotify_rm_watch */ + NULL, /* 256: migrate_pages */ + NULL, /* 257: openat */ + NULL, /* 258: mkdirat */ + lx_mknodat, /* 259: mknodat */ + NULL, /* 260: fchownat */ + lx_futimesat, /* 261: futimesat */ + NULL, /* 262: fstatat64 */ + NULL, /* 263: unlinkat */ + NULL, /* 264: renameat */ + NULL, /* 265: linkat */ + NULL, /* 266: symlinkat */ + NULL, /* 267: readlinkat */ + NULL, /* 268: fchmodat */ + NULL, /* 269: faccessat */ + NULL, /* 270: pselect6 */ + NULL, /* 271: ppoll */ + NULL, /* 272: unshare */ + NULL, /* 273: set_robust_list */ + NULL, /* 274: get_robust_list */ + NULL, /* 275: splice */ + NULL, /* 276: tee */ + NULL, /* 277: sync_file_range */ + NULL, /* 278: vmsplice */ + NULL, /* 279: move_pages */ + lx_utimensat, /* 280: utimensat */ + NULL, /* 281: epoll_pwait */ + lx_signalfd, /* 282: signalfd */ + lx_timerfd_create, /* 283: timerfd_create */ + NULL, /* 284: eventfd */ + NULL, /* 285: fallocate */ + lx_timerfd_settime, /* 286: timerfd_settime */ + lx_timerfd_gettime, /* 287: timerfd_gettime */ + NULL, /* 288: accept4 */ + lx_signalfd4, /* 289: signalfd4 */ + NULL, /* 290: eventfd2 */ + NULL, /* 291: epoll_create1 */ + NULL, /* 292: dup3 */ + NULL, /* 293: pipe2 */ + lx_inotify_init1, /* 294: inotify_init1 */ + NULL, /* 295: preadv */ + NULL, /* 296: pwritev */ + lx_rt_tgsigqueueinfo, /* 297: rt_tgsigqueueinfo */ + NULL, /* 298: perf_event_open */ + NULL, /* 299: recvmmsg */ + NULL, /* 300: fanotify_init */ + NULL, /* 301: fanotify_mark */ + NULL, /* 302: prlimit64 */ + NULL, /* 303: name_to_handle_at */ + NULL, /* 304: open_by_handle_at */ + NULL, /* 305: clock_adjtime */ + NULL, /* 306: syncfs */ + NULL, /* 307: sendmmsg */ + NULL, /* 309: setns */ + NULL, /* 309: getcpu */ + NULL, /* 310: process_vm_readv */ + NULL, /* 311: process_vm_writev */ + NULL, /* 312: kcmp */ + NULL, /* 313: finit_module */ + NULL, /* 314: sched_setattr */ + NULL, /* 315: sched_getattr */ + NULL, /* 316: renameat2 */ + NULL, /* 317: seccomp */ + NULL, /* 318: getrandom */ + NULL, /* 319: memfd_create */ + NULL, /* 320: kexec_file_load */ + NULL, /* 321: bpf */ + NULL, /* 322: execveat */ + + /* XXX TBD gap then x32 syscalls from 512 - 544 */ +}; + +#else +/* The following is the 32-bit syscall table */ + +static lx_syscall_handler_t lx_handlers[] = { + NULL, /* 0: nosys */ + lx_exit, /* 1: exit */ + lx_fork, /* 2: fork */ + NULL, /* 3: read */ + NULL, /* 4: write */ + NULL, /* 5: open */ + NULL, /* 6: close */ + NULL, /* 7: waitpid */ + NULL, /* 8: creat */ + NULL, /* 9: link */ + NULL, /* 10: unlink */ + lx_execve, /* 11: execve */ + NULL, /* 12: chdir */ + NULL, /* 13: time */ + lx_mknod, /* 14: mknod */ + NULL, /* 15: chmod */ + NULL, /* 16: lchown16 */ + NULL, /* 17: break */ + NULL, /* 18: stat */ + NULL, /* 19: lseek */ + NULL, /* 20: getpid */ + lx_mount, /* 21: mount */ + NULL, /* 22: umount */ + NULL, /* 23: setuid16 */ + NULL, /* 24: getuid16 */ + NULL, /* 25: stime */ + NULL, /* 26: ptrace */ + NULL, /* 27: alarm */ + NULL, /* 28: fstat */ + NULL, /* 29: pause */ + lx_utime, /* 30: utime */ + NULL, /* 31: stty */ + NULL, /* 32: gtty */ + NULL, /* 33: access */ + NULL, /* 34: nice */ + NULL, /* 35: ftime */ + NULL, /* 36: sync */ + NULL, /* 37: kill */ + NULL, /* 38: rename */ + NULL, /* 39: mkdir */ + lx_rmdir, /* 40: rmdir */ + NULL, /* 41: dup */ + NULL, /* 42: pipe */ + NULL, /* 43: times */ + NULL, /* 44: prof */ + NULL, /* 45: brk */ + NULL, /* 46: setgid16 */ + NULL, /* 47: getgid16 */ + lx_signal, /* 48: signal */ + NULL, /* 49: geteuid16 */ + NULL, /* 50: getegid16 */ + NULL, /* 51: acct */ + NULL, /* 52: umount2 */ + NULL, /* 53: lock */ + NULL, /* 54: ioctl */ + NULL, /* 55: fcntl */ + NULL, /* 56: mpx */ + NULL, /* 57: setpgid */ + NULL, /* 58: ulimit */ + NULL, /* 59: olduname */ + NULL, /* 60: umask */ + NULL, /* 61: chroot */ + NULL, /* 62: ustat */ + NULL, /* 63: dup2 */ + NULL, /* 64: getppid */ + NULL, /* 65: getpgrp */ + NULL, /* 66: setsid */ + lx_sigaction, /* 67: sigaction */ + NULL, /* 68: sgetmask */ + NULL, /* 69: ssetmask */ + NULL, /* 70: setreuid16 */ + NULL, /* 71: setregid16 */ + lx_sigsuspend, /* 72: sigsuspend */ + lx_sigpending, /* 73: sigpending */ + NULL, /* 74: sethostname */ + NULL, /* 75: setrlimit */ + NULL, /* 76: getrlimit */ + NULL, /* 77: getrusage */ + NULL, /* 78: gettimeofday */ + lx_settimeofday, /* 79: settimeofday */ + lx_getgroups16, /* 80: getgroups16 */ + lx_setgroups16, /* 81: setgroups16 */ + NULL, /* 82: select */ + NULL, /* 83: symlink */ + NULL, /* 84: oldlstat */ + NULL, /* 85: readlink */ + NULL, /* 86: uselib */ + NULL, /* 87: swapon */ + NULL, /* 88: reboot */ + lx_readdir, /* 89: readdir */ + NULL, /* 90: mmap */ + NULL, /* 91: munmap */ + lx_truncate, /* 92: truncate */ + lx_ftruncate, /* 93: ftruncate */ + NULL, /* 94: fchmod */ + NULL, /* 95: fchown16 */ + NULL, /* 96: getpriority */ + NULL, /* 97: setpriority */ + NULL, /* 98: profil */ + lx_statfs, /* 99: statfs */ + lx_fstatfs, /* 100: fstatfs */ + NULL, /* 101: ioperm */ + NULL, /* 102: socketcall */ + NULL, /* 103: syslog */ + lx_setitimer, /* 104: setitimer */ + NULL, /* 105: getitimer */ + NULL, /* 106: stat */ + NULL, /* 107: lstat */ + NULL, /* 108: fstat */ + NULL, /* 109: uname */ + NULL, /* 110: oldiopl */ + NULL, /* 111: vhangup */ + NULL, /* 112: idle */ + NULL, /* 113: vm86old */ + NULL, /* 114: wait4 */ + NULL, /* 115: swapoff */ + NULL, /* 116: sysinfo */ + lx_ipc, /* 117: ipc */ + lx_fsync, /* 118: fsync */ + lx_sigreturn, /* 119: sigreturn */ + lx_clone, /* 120: clone */ + NULL, /* 121: setdomainname */ + NULL, /* 122: uname */ + NULL, /* 123: modify_ldt */ + lx_adjtimex, /* 124: adjtimex */ + NULL, /* 125: mprotect */ + lx_sigprocmask, /* 126: sigprocmask */ + NULL, /* 127: create_module */ + NULL, /* 128: init_module */ + NULL, /* 129: delete_module */ + NULL, /* 130: get_kernel_syms */ + NULL, /* 131: quotactl */ + NULL, /* 132: getpgid */ + NULL, /* 133: fchdir */ + NULL, /* 134: bdflush */ + lx_sysfs, /* 135: sysfs */ + NULL, /* 136: personality */ + NULL, /* 137: afs_syscall */ + NULL, /* 138: setfsuid16 */ + NULL, /* 139: setfsgid16 */ + NULL, /* 140: llseek */ + NULL, /* 141: getdents */ + NULL, /* 142: select */ + NULL, /* 143: flock */ + NULL, /* 144: msync */ + NULL, /* 145: readv */ + NULL, /* 146: writev */ + NULL, /* 147: getsid */ + lx_fdatasync, /* 148: fdatasync */ + lx_sysctl, /* 149: sysctl */ + NULL, /* 150: mlock */ + NULL, /* 151: munlock */ + NULL, /* 152: mlockall */ + NULL, /* 153: munlockall */ + NULL, /* 154: sched_setparam */ + NULL, /* 155: sched_getparam */ + NULL, /* 156: sched_setscheduler */ + NULL, /* 157: sched_getscheduler */ + NULL, /* 158: sched_yield */ + NULL, /* 159: sched_get_priority_max */ + NULL, /* 160: sched_get_priority_min */ + NULL, /* 161: sched_rr_get_interval */ + NULL, /* 162: nanosleep */ + NULL, /* 163: mremap */ + NULL, /* 164: setresuid16 */ + NULL, /* 165: getresuid16 */ + NULL, /* 166: vm86 */ + lx_query_module, /* 167: query_module */ + NULL, /* 168: poll */ + NULL, /* 169: nfsservctl */ + NULL, /* 170: setresgid16 */ + NULL, /* 171: getresgid16 */ + NULL, /* 172: prctl */ + lx_rt_sigreturn, /* 173: rt_sigreturn */ + lx_rt_sigaction, /* 174: rt_sigaction */ + lx_rt_sigprocmask, /* 175: rt_sigprocmask */ + lx_rt_sigpending, /* 176: rt_sigpending */ + lx_rt_sigtimedwait, /* 177: rt_sigtimedwait */ + lx_rt_sigqueueinfo, /* 178: rt_sigqueueinfo */ + lx_rt_sigsuspend, /* 179: rt_sigsuspend */ + NULL, /* 180: pread64 */ + NULL, /* 181: pwrite64 */ + NULL, /* 182: chown16 */ + NULL, /* 183: getcwd */ + lx_capget, /* 184: capget */ + lx_capset, /* 185: capset */ + lx_sigaltstack, /* 186: sigaltstack */ + lx_sendfile, /* 187: sendfile */ + NULL, /* 188: getpmsg */ + NULL, /* 189: putpmsg */ + lx_vfork, /* 190: vfork */ + NULL, /* 191: getrlimit */ + NULL, /* 192: mmap2 */ + lx_truncate64, /* 193: truncate64 */ + lx_ftruncate64, /* 194: ftruncate64 */ + NULL, /* 195: stat64 */ + NULL, /* 196: lstat64 */ + NULL, /* 197: fstat64 */ + NULL, /* 198: lchown */ + NULL, /* 199: getuid */ + NULL, /* 200: getgid */ + NULL, /* 201: geteuid */ + NULL, /* 202: getegid */ + NULL, /* 203: setreuid */ + NULL, /* 204: setregid */ + lx_getgroups, /* 205: getgroups */ + lx_setgroups, /* 206: setgroups */ + NULL, /* 207: fchown */ + NULL, /* 208: setresuid */ + NULL, /* 209: getresuid */ + NULL, /* 210: setresgid */ + NULL, /* 211: getresgid */ + NULL, /* 212: chown */ + NULL, /* 213: setuid */ + NULL, /* 214: setgid */ + NULL, /* 215: setfsuid */ + NULL, /* 216: setfsgid */ + NULL, /* 217: pivot_root */ + NULL, /* 218: mincore */ + NULL, /* 219: madvise */ + NULL, /* 220: getdents64 */ + NULL, /* 221: fcntl64 */ + NULL, /* 222: tux */ + NULL, /* 223: security */ + NULL, /* 224: gettid */ + NULL, /* 225: readahead */ + NULL, /* 226: setxattr */ + NULL, /* 227: lsetxattr */ + NULL, /* 228: fsetxattr */ + NULL, /* 229: getxattr */ + NULL, /* 230: lgetxattr */ + NULL, /* 231: fgetxattr */ + NULL, /* 232: listxattr */ + NULL, /* 233: llistxattr */ + NULL, /* 234: flistxattr */ + NULL, /* 235: removexattr */ + NULL, /* 236: lremovexattr */ + NULL, /* 237: fremovexattr */ + NULL, /* 238: tkill */ + lx_sendfile64, /* 239: sendfile64 */ + NULL, /* 240: futex */ + NULL, /* 241: sched_setaffinity */ + NULL, /* 242: sched_getaffinity */ + NULL, /* 243: set_thread_area */ + NULL, /* 244: get_thread_area */ + NULL, /* 245: io_setup */ + NULL, /* 246: io_destroy */ + NULL, /* 247: io_getevents */ + NULL, /* 248: io_submit */ + NULL, /* 249: io_cancel */ + NULL, /* 250: fadvise64 */ + NULL, /* 251: nosys */ + lx_group_exit, /* 252: group_exit */ + NULL, /* 253: lookup_dcookie */ + NULL, /* 254: epoll_create */ + NULL, /* 255: epoll_ctl */ + NULL, /* 256: epoll_wait */ + NULL, /* 257: remap_file_pages */ + NULL, /* 258: set_tid_address */ + NULL, /* 259: timer_create */ + lx_timer_settime, /* 260: timer_settime */ + lx_timer_gettime, /* 261: timer_gettime */ + lx_timer_getoverrun, /* 262: timer_getoverrun */ + lx_timer_delete, /* 263: timer_delete */ + NULL, /* 264: clock_settime */ + NULL, /* 265: clock_gettime */ + NULL, /* 266: clock_getres */ + lx_clock_nanosleep, /* 267: clock_nanosleep */ + lx_statfs64, /* 268: statfs64 */ + lx_fstatfs64, /* 269: fstatfs64 */ + NULL, /* 270: tgkill */ + lx_utimes, /* 271: utimes */ + NULL, /* 272: fadvise64_64 */ + NULL, /* 273: vserver */ + NULL, /* 274: mbind */ + NULL, /* 275: get_mempolicy */ + NULL, /* 276: set_mempolicy */ + NULL, /* 277: mq_open */ + NULL, /* 278: mq_unlink */ + NULL, /* 279: mq_timedsend */ + NULL, /* 280: mq_timedreceive */ + NULL, /* 281: mq_notify */ + NULL, /* 282: mq_getsetattr */ + NULL, /* 283: kexec_load */ + NULL, /* 284: waitid */ + NULL, /* 285: sys_setaltroot */ + NULL, /* 286: add_key */ + NULL, /* 287: request_key */ + NULL, /* 288: keyctl */ + NULL, /* 289: ioprio_set */ + NULL, /* 290: ioprio_get */ + lx_inotify_init, /* 291: inotify_init */ + lx_inotify_add_watch, /* 292: inotify_add_watch */ + lx_inotify_rm_watch, /* 293: inotify_rm_watch */ + NULL, /* 294: migrate_pages */ + NULL, /* 295: openat */ + NULL, /* 296: mkdirat */ + lx_mknodat, /* 297: mknodat */ + NULL, /* 298: fchownat */ + lx_futimesat, /* 299: futimesat */ + NULL, /* 300: fstatat64 */ + NULL, /* 301: unlinkat */ + NULL, /* 302: renameat */ + NULL, /* 303: linkat */ + NULL, /* 304: symlinkat */ + NULL, /* 305: readlinkat */ + NULL, /* 306: fchmodat */ + NULL, /* 307: faccessat */ + NULL, /* 308: pselect6 */ + NULL, /* 309: ppoll */ + NULL, /* 310: unshare */ + NULL, /* 311: set_robust_list */ + NULL, /* 312: get_robust_list */ + NULL, /* 313: splice */ + NULL, /* 314: sync_file_range */ + NULL, /* 315: tee */ + NULL, /* 316: vmsplice */ + NULL, /* 317: move_pages */ + NULL, /* 318: getcpu */ + NULL, /* 319: epoll_pwait */ + lx_utimensat, /* 320: utimensat */ + lx_signalfd, /* 321: signalfd */ + lx_timerfd_create, /* 322: timerfd_create */ + NULL, /* 323: eventfd */ + NULL, /* 324: fallocate */ + lx_timerfd_settime, /* 325: timerfd_settime */ + lx_timerfd_gettime, /* 326: timerfd_gettime */ + lx_signalfd4, /* 327: signalfd4 */ + NULL, /* 328: eventfd2 */ + NULL, /* 329: epoll_create1 */ + NULL, /* 330: dup3 */ + NULL, /* 331: pipe2 */ + lx_inotify_init1, /* 332: inotify_init1 */ + NULL, /* 333: preadv */ + NULL, /* 334: pwritev */ + lx_rt_tgsigqueueinfo, /* 335: rt_tgsigqueueinfo */ + NULL, /* 336: perf_event_open */ + NULL, /* 337: recvmmsg */ + NULL, /* 338: fanotify_init */ + NULL, /* 339: fanotify_mark */ + NULL, /* 340: prlimit64 */ + NULL, /* 341: name_to_handle_at */ + NULL, /* 342: open_by_handle_at */ + NULL, /* 343: clock_adjtime */ + NULL, /* 344: syncfs */ + NULL, /* 345: sendmmsg */ + NULL, /* 346: setns */ + NULL, /* 347: process_vm_readv */ + NULL, /* 348: process_vm_writev */ + NULL, /* 349: kcmp */ + NULL, /* 350: finit_module */ + NULL, /* 351: sched_setattr */ + NULL, /* 352: sched_getattr */ + NULL, /* 353: renameat2 */ + NULL, /* 354: seccomp */ + NULL, /* 355: getrandom */ + NULL, /* 356: memfd_create */ + NULL, /* 357: bpf */ + NULL, /* 358: execveat */ +}; +#endif diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d b/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d new file mode 100644 index 0000000000..14326e8f56 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d @@ -0,0 +1,39 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +provider lx { + probe debug(char *buf); + probe sigdeliver(int sig, void *lx_sigaction, void *lx_sigstack); + probe sigreturn(void *lx_ucontext, void *ucontext, uintptr_t sp); + + probe signal__delivery__frame__create(void *lx_sigdeliver_frame); + probe signal__delivery__frame__found(void *lx_sigdeliver_frame); + probe signal__delivery__frame__corrupt(void *lx_sigdeliver_frame); + + probe signal__post__handler(uintptr_t old_sp, uintptr_t new_sp); + + probe signal__altstack__enable(uintptr_t alt_sp); + probe signal__altstack__disable(); + + probe emulate__enter(void *ucp, int syscall_num, uintptr_t *args); + probe emulate__return(void *ucp, int syscall_num, uintptr_t ret, + uintptr_t errn); +}; + +#pragma D attributes Evolving/Evolving/ISA provider lx provider +#pragma D attributes Private/Private/Unknown provider lx module +#pragma D attributes Private/Private/Unknown provider lx function +#pragma D attributes Private/Private/ISA provider lx name +#pragma D attributes Private/Private/ISA provider lx args diff --git a/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers b/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers new file mode 100644 index 0000000000..9d2712a12f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# +# Copyright 2020 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +# +# Scope everything local -- our .init section is our only public interface. +# +SYMBOL_SCOPE { + local: + *; +}; diff --git a/usr/src/lib/brand/lx/lx_brand/common/misc.c b/usr/src/lib/brand/lx/lx_brand/common/misc.c new file mode 100644 index 0000000000..a223d0ac35 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/misc.c @@ -0,0 +1,359 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <priv.h> +#include <strings.h> +#include <sys/reboot.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/sysmacros.h> +#include <sys/systeminfo.h> +#include <sys/types.h> +#include <sys/shm.h> +#include <sys/socket.h> +#include <sys/inotify.h> +#include <sys/eventfd.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> +#include <sys/lx_fcntl.h> + +/* + * {get,set}groups16() - Handle the conversion between 16-bit Linux gids and + * 32-bit illumos gids. + */ +long +lx_getgroups16(uintptr_t p1, uintptr_t p2) +{ + int count = (int)p1; + lx_gid16_t *grouplist = (lx_gid16_t *)p2; + gid_t *grouplist32; + int ret; + int i; + + if (count < 0) + return (-EINVAL); + + grouplist32 = malloc(count * sizeof (gid_t)); + if (grouplist32 == NULL && count > 0) { + free(grouplist32); + return (-ENOMEM); + } + if ((ret = getgroups(count, grouplist32)) < 0) { + free(grouplist32); + return (-errno); + } + + /* we must not modify the list if the incoming count was 0 */ + if (count > 0) { + for (i = 0; i < ret; i++) + grouplist[i] = LX_GID32_TO_GID16(grouplist32[i]); + } + + free(grouplist32); + return (ret); +} + +long +lx_setgroups16(uintptr_t p1, uintptr_t p2) +{ + long rv; + int count = (int)p1; + lx_gid16_t *grouplist = NULL; + gid_t *grouplist32 = NULL; + int i; + + if ((grouplist = malloc(count * sizeof (lx_gid16_t))) == NULL) { + return (-ENOMEM); + } + if (uucopy((void *)p2, grouplist, count * sizeof (lx_gid16_t)) != 0) { + free(grouplist); + return (-EFAULT); + } + + grouplist32 = malloc(count * sizeof (gid_t)); + if (grouplist32 == NULL) { + free(grouplist); + return (-ENOMEM); + } + for (i = 0; i < count; i++) + grouplist32[i] = LX_GID16_TO_GID32(grouplist[i]); + + /* order matters here to get the correct errno back */ + if (count > NGROUPS_MAX_DEFAULT) { + free(grouplist); + free(grouplist32); + return (-EINVAL); + } + + rv = setgroups(count, grouplist32); + + free(grouplist); + free(grouplist32); + + return (rv != 0 ? -errno : 0); +} + +/* + * mknod() - Since we don't have the SYS_CONFIG privilege within a zone, the + * only mode we have to support is S_IFIFO. We also have to distinguish between + * an invalid type and insufficient privileges. + */ +#define LX_S_IFMT 0170000 +#define LX_S_IFDIR 0040000 +#define LX_S_IFCHR 0020000 +#define LX_S_IFBLK 0060000 +#define LX_S_IFREG 0100000 +#define LX_S_IFIFO 0010000 +#define LX_S_IFLNK 0120000 +#define LX_S_IFSOCK 0140000 + +/*ARGSUSED*/ +long +lx_mknod(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + char *path = (char *)p1; + lx_dev_t lx_dev = (lx_dev_t)p3; + struct sockaddr_un sockaddr; + struct stat statbuf; + mode_t mode, type; + dev_t dev; + int fd; + + type = ((mode_t)p2 & LX_S_IFMT); + mode = ((mode_t)p2 & 07777); + + switch (type) { + case 0: + case LX_S_IFREG: + /* create a regular file */ + if (stat(path, &statbuf) == 0) + return (-EEXIST); + + if (errno != ENOENT) + return (-errno); + + if ((fd = creat(path, mode)) < 0) + return (-errno); + + (void) close(fd); + return (0); + + case LX_S_IFSOCK: + /* + * Create a UNIX domain socket. + * + * Most programmers aren't even aware you can do this. + * + * Note you can also do this via illumos' mknod(2), but + * Linux allows anyone who can create a UNIX domain + * socket via bind(2) to create one via mknod(2); + * illumos requires the caller to be privileged. + */ + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return (-errno); + + if (stat(path, &statbuf) == 0) + return (-EEXIST); + + if (errno != ENOENT) + return (-errno); + + if (uucopy(path, &sockaddr.sun_path, + sizeof (sockaddr.sun_path)) < 0) + return (-errno); + + /* assure NULL termination of sockaddr.sun_path */ + sockaddr.sun_path[sizeof (sockaddr.sun_path) - 1] = '\0'; + sockaddr.sun_family = AF_UNIX; + + if (bind(fd, (struct sockaddr *)&sockaddr, + strlen(sockaddr.sun_path) + + sizeof (sockaddr.sun_family)) < 0) + return (-errno); + + (void) close(fd); + return (0); + + case LX_S_IFIFO: + dev = 0; + break; + + case LX_S_IFCHR: + case LX_S_IFBLK: + /* + * The "dev" RPM package wants to create all possible Linux + * device nodes, so just report its mknod()s as having + * succeeded if we're in install mode. + */ + if (lx_install != 0) { + lx_debug("lx_mknod: install mode spoofed creation of " + "Linux device [%lld, %lld]\n", + LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev)); + + return (0); + } + + dev = makedevice(LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev)); + break; + + default: + return (-EINVAL); + } + + return (mknod(path, mode | type, dev) ? -errno : 0); +} + +long +lx_execve(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + char *filename = (char *)p1; + char **argv = (char **)p2; + char **envp = (char **)p3; + char *nullist[] = { NULL }; + + if (argv == NULL) + argv = nullist; + + /* + * Emulate PR_SET_KEEPCAPS which is reset on execve. If this is not done + * the emulated capabilities could be reduced more than expected. + */ + (void) setpflags(PRIV_AWARE_RESET, 1); + + /* This is a normal exec call. */ + (void) execve(filename, argv, envp); + + return (-errno); +} + +long +lx_setgroups(uintptr_t p1, uintptr_t p2) +{ + int ng = (int)p1; + gid_t *glist = NULL; + int i, r; + + lx_debug("\tlx_setgroups(%d, 0x%p", ng, p2); + + if (ng > 0) { + if ((glist = (gid_t *)malloc(ng * sizeof (gid_t))) == NULL) + return (-ENOMEM); + + if (uucopy((void *)p2, glist, ng * sizeof (gid_t)) != 0) { + free(glist); + return (-errno); + } + + /* + * Linux doesn't check the validity of the group IDs, but + * illumos does. Change any invalid group IDs to a known, valid + * value (yuck). + */ + for (i = 0; i < ng; i++) { + if (glist[i] > MAXUID) + glist[i] = MAXUID; + } + } + + /* order matters here to get the correct errno back */ + if (ng > NGROUPS_MAX_DEFAULT) { + free(glist); + return (-EINVAL); + } + + r = syscall(SYS_brand, B_HELPER_SETGROUPS, ng, glist); + + free(glist); + return ((r == -1) ? -errno : r); +} + +long +lx_getgroups(int gidsetsize, gid_t *grouplist) +{ + int r; + + r = getgroups(gidsetsize, grouplist); + return ((r == -1) ? -errno : r); +} + +long +lx_inotify_add_watch(int fd, const char *pathname, uint32_t mask) +{ + int r; + + r = inotify_add_watch(fd, pathname, mask); + return ((r == -1) ? -errno : r); +} + +long +lx_inotify_init(void) +{ + int r; + + r = inotify_init(); + return ((r == -1) ? -errno : r); +} + +long +lx_inotify_init1(int flags) +{ + int r; + + r = inotify_init1(flags); + return ((r == -1) ? -errno : r); +} + +long +lx_inotify_rm_watch(int fd, int wd) +{ + int r; + + r = inotify_rm_watch(fd, wd); + return ((r == -1) ? -errno : r); +} + +long +lx_shmdt(char *shmaddr) +{ + int r; + + r = shmdt(shmaddr); + return ((r == -1) ? -errno : r); +} + +long +lx_utimes(const char *path, const struct timeval times[2]) +{ + int r; + + r = utimes(path, times); + return ((r == -1) ? -errno : r); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/module.c b/usr/src/lib/brand/lx/lx_brand/common/module.c new file mode 100644 index 0000000000..78a593712f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/module.c @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2014 Joyent, Inc. All rights reserved. + */ + +/* + * We don't support Linux modules, but we have to emulate enough of the system + * calls to show that we don't have any modules installed. + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +/* + * For query_module(), we provide an empty list of modules, and return ENOENT + * on any request for a specific module. + */ +#define LX_QM_MODULES 1 +#define LX_QM_DEPS 2 +#define LX_QM_REFS 3 +#define LX_QM_SYMBOLS 4 +#define LX_QM_INFO 5 + +/*ARGSUSED*/ +long +lx_query_module(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5) +{ + /* + * parameter p1 is the 'name' argument. + */ + int which = (int)p2; + char *buf = (char *)p3; + size_t bufsize = (size_t)p4; + size_t *ret = (size_t *)p5; + + switch (which) { + case 0: + /* + * Special case: always return 0 + */ + return (0); + + case LX_QM_MODULES: + /* + * Generate an empty list of modules. + */ + if (bufsize && buf) + buf[0] = '\0'; + if (ret) + *ret = 0; + return (0); + + case LX_QM_DEPS: + case LX_QM_REFS: + case LX_QM_SYMBOLS: + case LX_QM_INFO: + /* + * Any requests for specific module information return ENOENT. + */ + return (-ENOENT); + + default: + return (-EINVAL); + } +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/mount.c b/usr/src/lib/brand/lx/lx_brand/common/mount.c new file mode 100644 index 0000000000..6ecd5cfc4f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mount.c @@ -0,0 +1,540 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. + */ + +#include <assert.h> +#include <errno.h> +#include <strings.h> +#include <nfs/mount.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> + +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> +#include <sys/lx_mount.h> + +/* + * support definitions + */ +union fh_buffer { + struct nfs_fid fh2; + struct nfs_fh3 fh3; + char fh_data[NFS3_FHSIZE + 2]; +}; + +static int +i_add_option(char *option, char *buf, size_t buf_size) +{ + char *fmt_str = NULL; + + assert((option != NULL) && (strlen(option) > 0)); + assert((buf != NULL) && (buf_size > 0)); + + if (buf[0] == '\0') { + fmt_str = "%s"; + } else { + fmt_str = ",%s"; + } + + buf_size -= strlen(buf); + buf += strlen(buf); + + /*LINTED*/ + if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1)) + return (-EOVERFLOW); + return (0); +} + +static int +i_add_option_int(char *option, int val, char *buf, size_t buf_size) +{ + char *fmt_str = NULL; + + assert((option != NULL) && (strlen(option) > 0)); + assert((buf != NULL) && (buf_size > 0)); + + if (buf[0] == '\0') { + fmt_str = "%s=%d"; + } else { + fmt_str = ",%s=%d"; + } + + buf_size -= strlen(buf); + buf += strlen(buf); + + /*LINTED*/ + if (snprintf(buf, buf_size, fmt_str, option, val) > (buf_size - 1)) + return (-EOVERFLOW); + return (0); +} + +static int +i_make_nfs_args(lx_nfs_mount_data_t *lx_nmd, struct nfs_args *nfs_args, + struct netbuf *nfs_args_addr, struct knetconfig *nfs_args_knconf, + union fh_buffer *nfs_args_fh, struct sec_data *nfs_args_secdata, + char *fstype, char *options, int options_size) +{ + struct stat statbuf; + int i, rv, use_tcp; + + /* Sanity check the incomming Linux request. */ + if ((lx_nmd->nmd_rsize < 0) || (lx_nmd->nmd_wsize < 0) || + (lx_nmd->nmd_timeo < 0) || (lx_nmd->nmd_retrans < 0) || + (lx_nmd->nmd_acregmin < 0) || (lx_nmd->nmd_acregmax < 0) || + (lx_nmd->nmd_acdirmax < 0)) { + return (-EINVAL); + } + + /* + * Additional sanity checks of incomming request. + * + * Some of the sanity checks below should probably return + * EINVAL (or some other error code) instead or ENOTSUP, + * but without experiminting on Linux to see how it + * deals with certain strange values there is no way + * to really know what we should return, hence we return + * ENOTSUP to tell us that eventually if we see some + * application hitting the problem we can go to a real + * Linux system, figure out how it deals with the situation + * and update our code to handle it in the same fashion. + */ + if (lx_nmd->nmd_version != 4) { + lx_unsupported("unsupported nfs mount request, " + "unrecognized NFS mount structure: %d\n", + lx_nmd->nmd_version); + return (-ENOTSUP); + } + if ((lx_nmd->nmd_flags & ~LX_NFS_MOUNT_SUPPORTED) != 0) { + lx_unsupported("unsupported nfs mount request, " + "flags: 0x%x\n", lx_nmd->nmd_flags); + return (-ENOTSUP); + } + if (lx_nmd->nmd_addr.sin_family != AF_INET) { + lx_unsupported("unsupported nfs mount request, " + "transport address family: 0x%x\n", + lx_nmd->nmd_addr.sin_family); + return (-ENOTSUP); + } + for (i = 0; i < LX_NMD_MAXHOSTNAMELEN; i++) { + if (lx_nmd->nmd_hostname[i] == '\0') + break; + } + if (i == 0) { + lx_unsupported("unsupported nfs mount request, " + "no hostname specified\n"); + return (-ENOTSUP); + } + if (i == LX_NMD_MAXHOSTNAMELEN) { + lx_unsupported("unsupported nfs mount request, " + "hostname not terminated\n"); + return (-ENOTSUP); + } + if (lx_nmd->nmd_namlen < i) { + lx_unsupported("unsupported nfs mount request, " + "invalid namlen value: 0x%x\n", lx_nmd->nmd_namlen); + return (-ENOTSUP); + } + if (lx_nmd->nmd_bsize != 0) { + lx_unsupported("unsupported nfs mount request, " + "bsize value: 0x%x\n", lx_nmd->nmd_bsize); + return (-ENOTSUP); + } + + /* Initialize and clear the output structure pointers passed in. */ + bzero(nfs_args, sizeof (*nfs_args)); + bzero(nfs_args_addr, sizeof (*nfs_args_addr)); + bzero(nfs_args_knconf, sizeof (*nfs_args_knconf)); + bzero(nfs_args_fh, sizeof (*nfs_args_fh)); + bzero(nfs_args_secdata, sizeof (*nfs_args_secdata)); + nfs_args->addr = nfs_args_addr; + nfs_args->knconf = nfs_args_knconf; + nfs_args->fh = (caddr_t)nfs_args_fh; + nfs_args->nfs_ext_u.nfs_extB.secdata = nfs_args_secdata; + + /* Check if we're using tcp. */ + use_tcp = (lx_nmd->nmd_flags & LX_NFS_MOUNT_TCP) ? 1 : 0; + + /* + * These seem to be the default flags used by Solaris for v2 and v3 + * nfs mounts. + * + * Don't bother with NFSMNT_TRYRDMA since we always specify a + * transport (either udp or tcp). + */ + nfs_args->flags = NFSMNT_NEWARGS | NFSMNT_KNCONF | NFSMNT_INT | + NFSMNT_HOSTNAME; + + /* Translate some Linux mount flags into Solaris mount flags. */ + if (lx_nmd->nmd_flags & LX_NFS_MOUNT_SOFT) + nfs_args->flags |= NFSMNT_SOFT; + if (lx_nmd->nmd_flags & LX_NFS_MOUNT_INTR) + nfs_args->flags |= NFSMNT_INT; + if (lx_nmd->nmd_flags & LX_NFS_MOUNT_POSIX) + nfs_args->flags |= NFSMNT_POSIX; + if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOCTO) + nfs_args->flags |= NFSMNT_NOCTO; + if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOAC) + nfs_args->flags |= NFSMNT_NOAC; + if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NONLM) + nfs_args->flags |= NFSMNT_LLOCK; + + if ((lx_nmd->nmd_flags & LX_NFS_MOUNT_VER3) != 0) { + (void) strcpy(fstype, "nfs3"); + if ((rv = i_add_option_int("vers", 3, + options, options_size)) != 0) + return (rv); + + if (lx_nmd->nmd_root.lx_fh3_length > + sizeof (nfs_args_fh->fh3.fh3_u.data)) { + lx_unsupported("unsupported nfs mount request, " + "nfs file handle length: 0x%x\n", + lx_nmd->nmd_root.lx_fh3_length); + return (-ENOTSUP); + } + + /* Set the v3 file handle info. */ + nfs_args_fh->fh3.fh3_length = lx_nmd->nmd_root.lx_fh3_length; + bcopy(&lx_nmd->nmd_root.lx_fh3_data, + nfs_args_fh->fh3.fh3_u.data, + lx_nmd->nmd_root.lx_fh3_length); + } else { + /* + * Assume nfs v2. Note that this could also be a v1 + * mount request but there doesn't seem to be any difference + * in the parameters passed to the Linux mount system + * call for v1 or v2 mounts so there is no way of really + * knowing. + */ + (void) strcpy(fstype, "nfs"); + if ((rv = i_add_option_int("vers", 2, + options, options_size)) != 0) + return (rv); + + /* Solaris seems to add this flag when using v2. */ + nfs_args->flags |= NFSMNT_SECDEFAULT; + + /* Set the v2 file handle info. */ + bcopy(&lx_nmd->nmd_old_root, + nfs_args_fh, sizeof (nfs_args_fh->fh2)); + } + + /* + * We can't use getnetconfig() here because there is no netconfig + * database in linux. + */ + nfs_args_knconf->knc_protofmly = "inet"; + if (use_tcp) { + /* + * TCP uses NC_TPI_COTS_ORD semantics. + * See /etc/netconfig. + */ + nfs_args_knconf->knc_semantics = NC_TPI_COTS_ORD; + nfs_args_knconf->knc_proto = "tcp"; + if ((rv = i_add_option("proto=tcp", + options, options_size)) != 0) + return (rv); + if (stat("/dev/tcp", &statbuf) != 0) + return (-errno); + nfs_args_knconf->knc_rdev = statbuf.st_rdev; + } else { + /* + * Assume UDP. UDP uses NC_TPI_CLTS semantics. + * See /etc/netconfig. + */ + nfs_args_knconf->knc_semantics = NC_TPI_CLTS; + nfs_args_knconf->knc_proto = "udp"; + if ((rv = i_add_option("proto=udp", + options, options_size)) != 0) + return (rv); + if (stat("/dev/udp", &statbuf) != 0) + return (-errno); + nfs_args_knconf->knc_rdev = statbuf.st_rdev; + } + + /* Set the server address. */ + nfs_args_addr->maxlen = nfs_args_addr->len = + sizeof (struct sockaddr_in); + nfs_args_addr->buf = (char *)&lx_nmd->nmd_addr; + + /* Set the server hostname string. */ + nfs_args->hostname = lx_nmd->nmd_hostname; + + /* Translate Linux nfs mount parameters into Solaris mount options. */ + if (lx_nmd->nmd_rsize != LX_NMD_DEFAULT_RSIZE) { + if ((rv = i_add_option_int("rsize", lx_nmd->nmd_rsize, + options, options_size)) != 0) + return (rv); + nfs_args->rsize = lx_nmd->nmd_rsize; + nfs_args->flags |= NFSMNT_RSIZE; + } + if (lx_nmd->nmd_wsize != LX_NMD_DEFAULT_WSIZE) { + if ((rv = i_add_option_int("wsize", lx_nmd->nmd_wsize, + options, options_size)) != 0) + return (rv); + nfs_args->wsize = lx_nmd->nmd_wsize; + nfs_args->flags |= NFSMNT_WSIZE; + } + if ((rv = i_add_option_int("timeo", lx_nmd->nmd_timeo, + options, options_size)) != 0) + return (rv); + nfs_args->timeo = lx_nmd->nmd_timeo; + nfs_args->flags |= NFSMNT_TIMEO; + if ((rv = i_add_option_int("retrans", lx_nmd->nmd_retrans, + options, options_size)) != 0) + return (rv); + nfs_args->retrans = lx_nmd->nmd_retrans; + nfs_args->flags |= NFSMNT_RETRANS; + if ((rv = i_add_option_int("acregmin", lx_nmd->nmd_acregmin, + options, options_size)) != 0) + return (rv); + nfs_args->acregmin = lx_nmd->nmd_acregmin; + nfs_args->flags |= NFSMNT_ACREGMIN; + if ((rv = i_add_option_int("acregmax", lx_nmd->nmd_acregmax, + options, options_size)) != 0) + return (rv); + nfs_args->acregmax = lx_nmd->nmd_acregmax; + nfs_args->flags |= NFSMNT_ACREGMAX; + if ((rv = i_add_option_int("acdirmin", lx_nmd->nmd_acdirmin, + options, options_size)) != 0) + return (rv); + nfs_args->acdirmin = lx_nmd->nmd_acdirmin; + nfs_args->flags |= NFSMNT_ACDIRMIN; + if ((rv = i_add_option_int("acdirmax", lx_nmd->nmd_acdirmax, + options, options_size)) != 0) + return (rv); + nfs_args->acdirmax = lx_nmd->nmd_acdirmax; + nfs_args->flags |= NFSMNT_ACDIRMAX; + + /* We only support nfs with a security type of AUTH_SYS. */ + nfs_args->nfs_args_ext = NFS_ARGS_EXTB; + nfs_args_secdata->secmod = AUTH_SYS; + nfs_args_secdata->rpcflavor = AUTH_SYS; + nfs_args_secdata->flags = 0; + nfs_args_secdata->uid = 0; + nfs_args_secdata->data = NULL; + nfs_args->nfs_ext_u.nfs_extB.next = NULL; + + /* + * The Linux nfs mount command seems to pass an open socket fd + * to the kernel during the mount system call. We don't need + * this fd on Solaris so just close it. + */ + (void) close(lx_nmd->nmd_fd); + + return (0); +} + +/* + * The user-level mount(2) code is only used to support NFS mounts. All other + * fstypes are handled in-kernel. + */ +long +lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5) +{ + /* Linux input arguments. */ + const char *sourcep = (const char *)p1; + const char *targetp = (const char *)p2; + const char *fstypep = (const char *)p3; + unsigned int flags = (unsigned int)p4; + const void *datap = (const void *)p5; + + char source[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1]; + char target[MAXPATHLEN]; + char fstype[8], options[MAX_MNTOPT_STR]; + int sflags, rv; + long res; + + lx_nfs_mount_data_t lx_nmd; + struct nfs_args nfs_args; + struct netbuf nfs_args_addr; + struct knetconfig nfs_args_knconf; + union fh_buffer nfs_args_fh; + struct sec_data nfs_args_secdata; + char *sdataptr = NULL; + int sdatalen = 0; + int vers; + + /* Initialize illumos mount arguments. */ + sflags = MS_OPTIONSTR; + options[0] = '\0'; + sdatalen = 0; + + /* Copy in parameters that are always present. */ + rv = uucopystr((void *)sourcep, &source, sizeof (source)); + if ((rv == -1) || (rv == sizeof (source))) + return (-EFAULT); + + rv = uucopystr((void *)targetp, &target, sizeof (target)); + if ((rv == -1) || (rv == sizeof (target))) + return (-EFAULT); + + rv = uucopystr((void *)fstypep, &fstype, sizeof (fstype)); + if ((rv == -1) || (rv == sizeof (fstype))) + return (-EFAULT); + + lx_debug("\tlinux mount source: %s", source); + lx_debug("\tlinux mount target: %s", target); + lx_debug("\tlinux mount fstype: %s", fstype); + + /* The in-kernel mount code should only call us for an NFS mount. */ + assert(strcmp(fstype, "nfs") == 0 || strcmp(fstype, "nfs4") == 0); + + /* + * While SunOS is picky about mount(2) target paths being absolute, + * Linux is not so strict. In order to facilitate this looser + * requirement, the cwd is prepended to non-absolute target paths. + */ + if (target[0] != '/') { + char *cpath, *buf = NULL; + int len; + + if ((cpath = getcwd(NULL, MAXPATHLEN)) == NULL) { + return (-ENOMEM); + } + len = asprintf(&buf, "%s/%s", cpath, target); + free(cpath); + if (len < 0) { + return (-ENOMEM); + } else if (len >= MAXPATHLEN) { + free(buf); + return (-ENAMETOOLONG); + } + (void) strlcpy(target, buf, sizeof (target)); + free(buf); + } + + /* Make sure we support the requested mount flags. */ + if ((flags & ~LX_MS_SUPPORTED) != 0) { + lx_unsupported("unsupported mount flags: 0x%x", flags); + return (-ENOTSUP); + } + + /* + * Copy in Linux mount options. Note that for older Linux kernels + * (pre 2.6.23) the mount options pointer (which normally points to a + * string) points to a structure which is populated by the user-level + * code after it has done the preliminary RPCs (similar to how our NFS + * mount cmd works). For newer kernels the options pointer is just a + * string of options. We're unlikely to actually emulate a kernel that + * uses the old style but support is kept and handled in + * i_make_nfs_args(). The new style handling is implemented in + * lx_nfs_mount(). The user-level mount caller is in charge of + * determining the format in which it passes the data parameter. + */ + if (datap == NULL) + return (-EINVAL); + if (uucopy((void *)datap, &vers, sizeof (int)) < 0) + return (-errno); + + /* + * As described above, the data parameter might be a versioned lx_nmd + * structure or (most likely on a modern distribution) it is a string. + */ + if (vers < 1 || vers > 6) { + /* + * Handle the modern style with options as a string, make the + * preliminary RPC calls and do the native mount all within + * lx_nfs_mount(). + */ + if (uucopystr((void *)datap, options, sizeof (options)) < 0) + return (-errno); + return (lx_nfs_mount(source, target, fstype, flags, options)); + } + + /* + * This is an old style NFS mount call and we only support v4. + */ + if (vers != 4) { + lx_unsupported("unsupported nfs mount request version: %d\n", + vers); + return (-ENOTSUP); + } + + if (uucopy((void *)datap, &lx_nmd, sizeof (lx_nmd)) < 0) + return (-errno); + + /* + * For illumos NFS mounts, the kernel expects a special structure, but + * a pointer to this structure is passed in via an extra parameter + * (sdataptr below.) + */ + if ((rv = i_make_nfs_args(&lx_nmd, &nfs_args, &nfs_args_addr, + &nfs_args_knconf, &nfs_args_fh, &nfs_args_secdata, fstype, options, + sizeof (options))) != 0) + return (rv); + + /* + * For the following old-style NFS mount we need to tell the mount + * system call to expect extra parameters. + */ + sflags |= MS_DATA; + sdataptr = (char *)&nfs_args; + sdatalen = sizeof (nfs_args); + + /* Linux seems to always allow overlay mounts */ + sflags |= MS_OVERLAY; + + /* Convert some Linux flags to illumos flags. */ + if (flags & LX_MS_RDONLY) + sflags |= MS_RDONLY; + if (flags & LX_MS_NOSUID) + sflags |= MS_NOSUID; + if (flags & LX_MS_REMOUNT) + sflags |= MS_REMOUNT; + + /* + * Convert some Linux flags to illumos option strings. + */ + if (flags & LX_MS_STRICTATIME) { + /* + * The "strictatime" mount option ensures that none of the + * weaker atime-related mode options are in effect. + */ + flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME); + } + if ((flags & LX_MS_NODEV) && + ((rv = i_add_option("nodev", options, sizeof (options))) != 0)) + return (rv); + if ((flags & LX_MS_NOEXEC) && + ((rv = i_add_option("noexec", options, sizeof (options))) != 0)) + return (rv); + if ((flags & LX_MS_NOATIME) && + ((rv = i_add_option("noatime", options, sizeof (options))) != 0)) + return (rv); + + lx_debug("\tsolaris mount fstype: %s", fstype); + lx_debug("\tsolaris mount options: \"%s\"", options); + + res = mount(source, target, sflags, fstype, sdataptr, sdatalen, + options, sizeof (options)); + + return ((res == 0) ? 0 : -errno); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c b/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c new file mode 100644 index 0000000000..ca90a80e5f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c @@ -0,0 +1,1506 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * NFS mount syscall support + * + * The illumos NFS user-level mount code encapsulates a significant amount of + * functionality into librpc and libnsl. This includes a variety of functions + * to perform lookups in the various /etc configuration files. For v2/v3 in + * particular, the library code must make a call to the server's 'mountd' to + * obtain a file handle to pass into the mount(2) syscall. There can be a + * variety of calls to the server's 'rpcbind' made by the libraries during the + * preliminaries before calling mount(2). All of the logic for falling back + * when determining which version to use (when none is explicitly provided), as + * well as retries when the server is not responding, is encapsulated in the + * libraries. + * + * For Linux, much of this functionality is also included in the user-level + * mount code, and thus not of concern to us at the Linux syscall level. The + * major difference is that the RPC to the 'mountd' to get the file handle for + * v2/v3 is made within the kernel as part of the mount(2) syscall. However, + * the Linux user-level code will perform all of the logical name lookups (e.g. + * hostname to IP address), will make the 'rpcbind' call to determine the + * server's 'mountd' protocol and port, and will handle the retry logic. + * + * Thus, when we reach our code here, we don't need to do any name lookups in + * any of the /etc files and we never need to call 'rpcbind'. We only need to + * make the RPC to get the file handle for v2/v3 mounts. We're still dependent + * on librpc/libnsl to make this RPC, but our overall complexity is much less + * than what is seen with the native mount library usage. In addition, we also + * have to convert the Linux mount arguments into our native format. Because + * we're really just making the RPC to get a file handle and reformatting the + * mount arguments, this code should be amenable to living in-kernel at some + * point. + * + * Finally, in most of the functions below, when the code refers to the + * hostname we're really working with the IP addr that the Linux user-level + * mount command passed in to us. + */ + +/* + * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +/* + * Copyright 2017 Joyent, Inc. + */ + +#define NFSCLIENT +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/param.h> +#include <rpc/rpc.h> +#include <errno.h> +#include <netdb.h> +#include <sys/mount.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <nfs/nfs.h> +#include <nfs/mount.h> +#include <rpcsvc/mount.h> +#include <sys/pathconf.h> +#include <netdir.h> +#include <netconfig.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <netinet/in.h> +#include <nfs/nfs_sec.h> +#include <rpcsvc/daemon_utils.h> +#include <rpcsvc/nfs4_prot.h> +#include <limits.h> +#include <nfs/nfssys.h> +#include <strings.h> +#include <assert.h> +#include <sys/lx_mount.h> +#include <sys/lx_misc.h> +#include <sys/syscall.h> + +#ifndef NFS_VERSMAX +#define NFS_VERSMAX 4 +#endif +#ifndef NFS_VERSMIN +#define NFS_VERSMIN 2 +#endif + +#define RET_OK 0 +#define RET_RETRY 32 +#define RET_ERR 33 +#define RET_PROTOUNSUPP 34 +#define RET_MNTERR 1000 +#define ERR_PROTO_NONE 0 +#define ERR_PROTO_INVALID 901 +#define ERR_PROTO_UNSUPP 902 +#define ERR_NETPATH 903 +#define ERR_NOHOST 904 +#define ERR_RPCERROR 905 + +typedef struct err_ret { + int error_type; + int error_value; +} err_ret_t; + +#define SET_ERR_RET(errst, etype, eval) \ + { \ + (errst)->error_type = etype; \ + (errst)->error_value = eval; \ + } + +/* + * Built-in netconfig table. + */ +#define N_NETCONF_ENTS 4 +static struct netconfig nca[N_NETCONF_ENTS] = { + {"udp6", NC_TPI_CLTS, 1, "inet6", "udp", "/dev/udp6", 0, NULL}, + {"tcp6", NC_TPI_COTS_ORD, 1, "inet6", "tcp", "/dev/tcp6", 0, NULL}, + {"udp", NC_TPI_CLTS, 1, "inet", "udp", "/dev/udp", 0, NULL}, + {"tcp", NC_TPI_COTS_ORD, 1, "inet", "tcp", "/dev/tcp", 0, NULL} +}; + +/* + * Mapping table of Linux NFS mount options to the corresponding Illumos + * option. The nmo_argtyp field tells us how to handle the argument. + */ +typedef enum map_mount_opt_type { + MOUNT_OPT_INVALID = 0, + MOUNT_OPT_PASTHRU = 1, + MOUNT_OPT_IGNORE = 2, + MOUNT_OPT_TOKEN = 3, + MOUNT_OPT_HAS_ARG = 4 +} map_mount_opt_type_t; + +typedef struct nfs_map_opt { + char *nmo_lx_opt; + char *nmo_il_opt; + map_mount_opt_type_t nmo_argtyp; +} nfs_map_opt_t; + +static nfs_map_opt_t nmo_tab[] = { + {"ac", NULL, MOUNT_OPT_IGNORE}, + {"acdirmax", NULL, MOUNT_OPT_PASTHRU}, + {"acdirmin", NULL, MOUNT_OPT_PASTHRU}, + {"acl", NULL, MOUNT_OPT_INVALID}, + {"acregmax", NULL, MOUNT_OPT_PASTHRU}, + {"acregmin", NULL, MOUNT_OPT_PASTHRU}, + {"actimeo", NULL, MOUNT_OPT_PASTHRU}, + {"bg", NULL, MOUNT_OPT_IGNORE}, + {"cto", NULL, MOUNT_OPT_IGNORE}, + {"fg", NULL, MOUNT_OPT_IGNORE}, + {"fsc", NULL, MOUNT_OPT_IGNORE}, + {"hard", NULL, MOUNT_OPT_PASTHRU}, + {"intr", NULL, MOUNT_OPT_PASTHRU}, + {"lock", NULL, MOUNT_OPT_IGNORE}, + {"lookupcache", NULL, MOUNT_OPT_INVALID}, + {"local_lock=%s", NULL, MOUNT_OPT_INVALID }, + {"migration", NULL, MOUNT_OPT_INVALID}, + {"minorversion", NULL, MOUNT_OPT_INVALID}, + {"mountaddr", NULL, MOUNT_OPT_INVALID}, + {"mounthost", NULL, MOUNT_OPT_INVALID}, + {"mountport", NULL, MOUNT_OPT_PASTHRU}, + {"mountproto", NULL, MOUNT_OPT_PASTHRU}, + {"mountvers", NULL, MOUNT_OPT_PASTHRU}, + {"namlen", NULL, MOUNT_OPT_INVALID}, + {"nfsvers", NULL, MOUNT_OPT_INVALID}, + {"noac", NULL, MOUNT_OPT_PASTHRU}, + {"noacl", NULL, MOUNT_OPT_INVALID}, + {"nocto", NULL, MOUNT_OPT_PASTHRU}, + {"nofsc", NULL, MOUNT_OPT_IGNORE}, + {"nointr", NULL, MOUNT_OPT_PASTHRU}, + {"nolock", "llock", MOUNT_OPT_TOKEN}, + {"nomigration", NULL, MOUNT_OPT_INVALID}, + {"noposix", NULL, MOUNT_OPT_IGNORE}, + {"nordirplus", NULL, MOUNT_OPT_IGNORE}, + {"noresvport", NULL, MOUNT_OPT_INVALID}, + {"nosharecache", NULL, MOUNT_OPT_IGNORE}, + {"port", NULL, MOUNT_OPT_PASTHRU}, + {"posix", NULL, MOUNT_OPT_PASTHRU}, + {"proto", NULL, MOUNT_OPT_PASTHRU}, + {"rdirplus", NULL, MOUNT_OPT_IGNORE}, + {"rdma", "proto=rdma", MOUNT_OPT_TOKEN}, + {"resvport", NULL, MOUNT_OPT_IGNORE}, + {"retrans", NULL, MOUNT_OPT_PASTHRU}, + {"retry", NULL, MOUNT_OPT_IGNORE}, + {"rsize", NULL, MOUNT_OPT_PASTHRU}, + {"sec", NULL, MOUNT_OPT_PASTHRU}, + {"sharecache", NULL, MOUNT_OPT_IGNORE}, + {"sloppy", NULL, MOUNT_OPT_IGNORE}, + {"soft", NULL, MOUNT_OPT_PASTHRU}, + {"tcp", "proto=tcp", MOUNT_OPT_TOKEN}, + {"timeo", NULL, MOUNT_OPT_PASTHRU}, + {"udp", "proto=udp", MOUNT_OPT_TOKEN}, + {"vers", NULL, MOUNT_OPT_PASTHRU}, + {"wsize", NULL, MOUNT_OPT_PASTHRU}, + {NULL, NULL, MOUNT_OPT_INVALID} +}; + +/* + * This struct is used to keep track of misc. variables which are set deep + * in one function then referenced someplace else. We pass this around to + * avoid the use of global variables as is done the the NFS mount command. + * + * The nfsvers variables control the NFS version number to be used. + * + * nmd_nfsvers defaults to 0 which means to use the highest number that + * both the client and the server support. It can also be set to + * a particular value, either 2, 3, or 4 to indicate the version + * number of choice. If the server (or the client) do not support + * the version indicated, then the mount attempt will be failed. + */ +typedef struct nfs_mnt_data { + int nmd_posix; + ushort_t nmd_nfs_port; + char *nmd_nfs_proto; + ushort_t nmd_mnt_port; + char *nmd_mnt_proto; + char *nmd_fstype; + seconfig_t nmd_nfs_sec; + int nmd_sec_opt; /* any security option ? */ + int nmd_nolock_opt; /* 'nolock' specified */ + rpcvers_t nmd_mnt_vers; + rpcvers_t nmd_nfsvers; +} nfs_mnt_data_t; + +/* number of transports to try */ +#define MNT_PREF_LISTLEN 2 +#define FIRST_TRY 1 +#define SECOND_TRY 2 + +#define BIGRETRY 10000 + +/* maximum length of RPC header for NFS messages */ +#define NFS_RPC_HDR 432 + +extern int __clnt_bindresvport(CLIENT *); + +static int set_args(int *, struct nfs_args *, char *, char *, nfs_mnt_data_t *); +static int get_fh(struct nfs_args *, char *, char *, nfs_mnt_data_t *); +static int make_secure(struct nfs_args *, nfs_mnt_data_t *); +static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **, + nfs_mnt_data_t *); + +static void +log_err(const char *fmt, ...) +{ + va_list ap; + char buf[128]; + int fd; + + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof (buf), fmt, ap); + va_end(ap); + + if ((fd = open("/dev/conslog", O_WRONLY)) != -1) { + (void) write(fd, buf, strlen(buf)); + (void) close(fd); + } +} + +static int +i_add_option(char *option, char *buf, size_t buf_size) +{ + int len; + char *fmt_str = NULL; + + if (buf[0] == '\0') { + fmt_str = "%s"; + } else { + fmt_str = ",%s"; + } + + len = strlen(buf); + buf_size -= len; + buf += len; + + /*LINTED*/ + if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1)) + return (-EOVERFLOW); + return (0); +} + +/* + * These options were initially derived from uts/common/fs/nfs/nfs_dlinet.c + * but have been extended to add additional Linux options. + */ +static char *optlist[] = { +#define OPT_RO 0 + MNTOPT_RO, +#define OPT_RW 1 + MNTOPT_RW, +#define OPT_QUOTA 2 + MNTOPT_QUOTA, +#define OPT_NOQUOTA 3 + MNTOPT_NOQUOTA, +#define OPT_SOFT 4 + MNTOPT_SOFT, +#define OPT_HARD 5 + MNTOPT_HARD, +#define OPT_SUID 6 + MNTOPT_SUID, +#define OPT_NOSUID 7 + MNTOPT_NOSUID, +#define OPT_GRPID 8 + MNTOPT_GRPID, +#define OPT_REMOUNT 9 + MNTOPT_REMOUNT, +#define OPT_NOSUB 10 + MNTOPT_NOSUB, +#define OPT_INTR 11 + MNTOPT_INTR, +#define OPT_NOINTR 12 + MNTOPT_NOINTR, +#define OPT_PORT 13 + MNTOPT_PORT, +#define OPT_SECURE 14 + MNTOPT_SECURE, +#define OPT_RSIZE 15 + MNTOPT_RSIZE, +#define OPT_WSIZE 16 + MNTOPT_WSIZE, +#define OPT_TIMEO 17 + MNTOPT_TIMEO, +#define OPT_RETRANS 18 + MNTOPT_RETRANS, +#define OPT_ACTIMEO 19 + MNTOPT_ACTIMEO, +#define OPT_ACREGMIN 20 + MNTOPT_ACREGMIN, +#define OPT_ACREGMAX 21 + MNTOPT_ACREGMAX, +#define OPT_ACDIRMIN 22 + MNTOPT_ACDIRMIN, +#define OPT_ACDIRMAX 23 + MNTOPT_ACDIRMAX, +#define OPT_BG 24 + MNTOPT_BG, +#define OPT_FG 25 + MNTOPT_FG, +#define OPT_RETRY 26 + MNTOPT_RETRY, +#define OPT_NOAC 27 + MNTOPT_NOAC, +#define OPT_NOCTO 28 + MNTOPT_NOCTO, +#define OPT_LLOCK 29 + MNTOPT_LLOCK, +#define OPT_POSIX 30 + MNTOPT_POSIX, +#define OPT_VERS 31 + MNTOPT_VERS, +#define OPT_PROTO 32 + MNTOPT_PROTO, +#define OPT_SEMISOFT 33 + MNTOPT_SEMISOFT, +#define OPT_NOPRINT 34 + MNTOPT_NOPRINT, +#define OPT_SEC 35 + MNTOPT_SEC, +#define OPT_LARGEFILES 36 + MNTOPT_LARGEFILES, +#define OPT_NOLARGEFILES 37 + MNTOPT_NOLARGEFILES, +#define OPT_PUBLIC 38 + MNTOPT_PUBLIC, +#define OPT_DIRECTIO 39 + MNTOPT_FORCEDIRECTIO, +#define OPT_NODIRECTIO 40 + MNTOPT_NOFORCEDIRECTIO, +#define OPT_XATTR 41 + MNTOPT_XATTR, +#define OPT_NOXATTR 42 + MNTOPT_NOXATTR, +#define OPT_DEVICES 43 + MNTOPT_DEVICES, +#define OPT_NODEVICES 44 + MNTOPT_NODEVICES, +#define OPT_SETUID 45 + MNTOPT_SETUID, +#define OPT_NOSETUID 46 + MNTOPT_NOSETUID, +#define OPT_EXEC 47 + MNTOPT_EXEC, +#define OPT_NOEXEC 48 + MNTOPT_NOEXEC, +#define OPT_MNT_VERS 49 + "mountvers", +#define OPT_MNT_PORT 50 + "mountport", +#define OPT_MNT_PROTO 51 + "mountproto", + + NULL +}; + +static int +convert_int(int *val, char *str) +{ + long lval; + + if (str == NULL || !isdigit(*str)) + return (-1); + + lval = strtol(str, &str, 10); + if (*str != '\0' || lval > INT_MAX) + return (-2); + + *val = (int)lval; + return (0); +} + +static int +set_args(int *mntflags, struct nfs_args *args, char *fshost, char *mntopts, + nfs_mnt_data_t *nmdp) +{ + char *saveopt, *optstr, *opts, *newopts, *val; + int num; + int largefiles = 0; + int invalid = 0; + int attrpref = 0; + int optlen, oldlen; + + args->flags = NFSMNT_INT; /* default is "intr" */ + args->flags |= NFSMNT_HOSTNAME; + args->flags |= NFSMNT_NEWARGS; /* using extented nfs_args structure */ + args->hostname = fshost; + + oldlen = strlen(mntopts); + optstr = opts = strdup(mntopts); + if (opts == NULL) + return (-ENOMEM); + /* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */ + optlen = oldlen + sizeof (MNTOPT_XATTR) + 1; + if (optlen > MAX_MNTOPT_STR) + return (-EINVAL); + + newopts = malloc(optlen); + if (opts == NULL || newopts == NULL) { + if (opts) + free(opts); + if (newopts) + free(newopts); + return (-EINVAL); + } + newopts[0] = '\0'; + + while (*opts) { + invalid = 0; + saveopt = opts; + switch (getsubopt(&opts, optlist, &val)) { + case OPT_RO: + *mntflags |= MS_RDONLY; + break; + case OPT_RW: + *mntflags &= ~(MS_RDONLY); + break; + case OPT_QUOTA: + case OPT_NOQUOTA: + break; + case OPT_SOFT: + args->flags |= NFSMNT_SOFT; + args->flags &= ~(NFSMNT_SEMISOFT); + break; + case OPT_SEMISOFT: + args->flags |= NFSMNT_SOFT; + args->flags |= NFSMNT_SEMISOFT; + break; + case OPT_HARD: + args->flags &= ~(NFSMNT_SOFT); + args->flags &= ~(NFSMNT_SEMISOFT); + break; + case OPT_SUID: + *mntflags &= ~(MS_NOSUID); + break; + case OPT_NOSUID: + *mntflags |= MS_NOSUID; + break; + case OPT_GRPID: + args->flags |= NFSMNT_GRPID; + break; + case OPT_REMOUNT: + *mntflags |= MS_REMOUNT; + break; + case OPT_INTR: + args->flags |= NFSMNT_INT; + break; + case OPT_NOINTR: + args->flags &= ~(NFSMNT_INT); + break; + case OPT_NOAC: + args->flags |= NFSMNT_NOAC; + break; + case OPT_PORT: + if (convert_int(&num, val) != 0) + goto badopt; + nmdp->nmd_nfs_port = num; + break; + + case OPT_NOCTO: + args->flags |= NFSMNT_NOCTO; + break; + + case OPT_RSIZE: + if (convert_int(&args->rsize, val) != 0) + goto badopt; + args->flags |= NFSMNT_RSIZE; + break; + case OPT_WSIZE: + if (convert_int(&args->wsize, val) != 0) + goto badopt; + args->flags |= NFSMNT_WSIZE; + break; + case OPT_TIMEO: + if (convert_int(&args->timeo, val) != 0) + goto badopt; + args->flags |= NFSMNT_TIMEO; + break; + case OPT_RETRANS: + if (convert_int(&args->retrans, val) != 0) + goto badopt; + args->flags |= NFSMNT_RETRANS; + break; + case OPT_ACTIMEO: + if (convert_int(&args->acregmax, val) != 0) + goto badopt; + args->acdirmin = args->acregmin = args->acdirmax + = args->acregmax; + args->flags |= NFSMNT_ACDIRMAX; + args->flags |= NFSMNT_ACREGMAX; + args->flags |= NFSMNT_ACDIRMIN; + args->flags |= NFSMNT_ACREGMIN; + break; + case OPT_ACREGMIN: + if (convert_int(&args->acregmin, val) != 0) + goto badopt; + args->flags |= NFSMNT_ACREGMIN; + break; + case OPT_ACREGMAX: + if (convert_int(&args->acregmax, val) != 0) + goto badopt; + args->flags |= NFSMNT_ACREGMAX; + break; + case OPT_ACDIRMIN: + if (convert_int(&args->acdirmin, val) != 0) + goto badopt; + args->flags |= NFSMNT_ACDIRMIN; + break; + case OPT_ACDIRMAX: + if (convert_int(&args->acdirmax, val) != 0) + goto badopt; + args->flags |= NFSMNT_ACDIRMAX; + break; + case OPT_BG: + /* Ignored as does Linux kernel */ + break; + case OPT_FG: + /* Ignored as does Linux kernel */ + break; + case OPT_RETRY: + /* Ignored as does Linux kernel */ + break; + case OPT_LLOCK: + args->flags |= NFSMNT_LLOCK; + break; + case OPT_POSIX: + nmdp->nmd_posix = 1; + break; + case OPT_VERS: + if (convert_int(&num, val) != 0) + goto badopt; + nmdp->nmd_nfsvers = (rpcvers_t)num; + break; + case OPT_PROTO: + if (val == NULL) + goto badopt; + + nmdp->nmd_nfs_proto = (char *)malloc(strlen(val)+1); + if (!nmdp->nmd_nfs_proto) + return (-EINVAL); + + (void) strncpy(nmdp->nmd_nfs_proto, val, strlen(val)+1); + break; + + case OPT_NOPRINT: + args->flags |= NFSMNT_NOPRINT; + break; + + case OPT_LARGEFILES: + largefiles = 1; + break; + + case OPT_NOLARGEFILES: + free(optstr); + return (-EINVAL); + + case OPT_SEC: + if (val == NULL) + return (-EINVAL); + /* + * We initialize the nfs_sec struct as if we had the + * basic /etc/nfssec.conf file. + */ + if (strcmp(val, "none") == 0) { + (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, + "none", MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = + nmdp->nmd_nfs_sec.sc_rpcnum = 0; + } else if (strcmp(val, "sys") == 0) { + (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, + "sys", MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = + nmdp->nmd_nfs_sec.sc_rpcnum = 1; + } else { + return (-EINVAL); + } + nmdp->nmd_sec_opt++; + break; + + case OPT_DIRECTIO: + args->flags |= NFSMNT_DIRECTIO; + break; + + case OPT_NODIRECTIO: + args->flags &= ~(NFSMNT_DIRECTIO); + break; + + case OPT_XATTR: + case OPT_NOXATTR: + /* + * VFS options; just need to get them into the + * new mount option string and note we've seen them + */ + attrpref = 1; + break; + + case OPT_MNT_VERS: + if (convert_int(&num, val) != 0) + goto badopt; + nmdp->nmd_mnt_vers = (rpcvers_t)num; + invalid = 1; /* Invalid as a native option */ + break; + + case OPT_MNT_PORT: + if (convert_int(&num, val) != 0) + goto badopt; + nmdp->nmd_mnt_port = num; + invalid = 1; /* Invalid as a native option */ + break; + + case OPT_MNT_PROTO: + if (val == NULL) + goto badopt; + nmdp->nmd_mnt_proto = strdup(val); + if (nmdp->nmd_mnt_proto == NULL) + return (-ENOMEM); + invalid = 1; /* Invalid as a native option */ + break; + + default: + invalid = 1; + break; + } + if (!invalid) { + if (newopts[0] != '\0') { + (void) strlcat(newopts, ",", optlen); + } + (void) strlcat(newopts, saveopt, optlen); + } + } + /* Default is to turn extended attrs on */ + if (!attrpref) { + if (newopts[0]) { + (void) strlcat(newopts, ",", optlen); + } + (void) strlcat(newopts, MNTOPT_XATTR, optlen); + } + (void) strlcpy(mntopts, newopts, oldlen); + free(newopts); + free(optstr); + + /* ensure that only one secure mode is requested */ + if (nmdp->nmd_sec_opt > 1) + return (-EINVAL); + + /* ensure that the user isn't trying to get large files over V2 */ + if (nmdp->nmd_nfsvers == NFS_VERSION && largefiles) + return (-EINVAL); + + if (nmdp->nmd_nfsvers == NFS_V4) { + /* + * NFSv4 specifies the TCP protocol and port 2049 - default to + * these. The user-level mount code is not expected to pass + * these in, but if it did, validate the proto value. + */ + if (nmdp->nmd_nfs_proto == NULL) { + nmdp->nmd_nfs_proto = strdup(NC_TCP); + if (nmdp->nmd_nfs_proto == NULL) + return (-ENOMEM); + + } else if (strcmp(nmdp->nmd_nfs_proto, NC_TCP) != 0) { + return (-EINVAL); + } + + } else { + /* + * The user-level mount code normally passes in the proto, but + * if it didn't for some reason, use a sensible default. + * Otherwise we normally just validate the proto value and we + * only support TCP or UDP. + */ + if (nmdp->nmd_nfs_proto == NULL) { + nmdp->nmd_nfs_proto = strdup(NC_TCP); + if (nmdp->nmd_nfs_proto == NULL) + return (-ENOMEM); + + } else if (strcmp(nmdp->nmd_nfs_proto, NC_TCP) != 0 && + strcmp(nmdp->nmd_nfs_proto, NC_UDP) != 0) { + return (-EINVAL); + } + } + + /* + * The user-level mount code only passes the port when it is + * non-standard. + */ + if (nmdp->nmd_nfs_port == 0) { + nmdp->nmd_nfs_port = NFS_PORT; + } + + return (0); + +badopt: + free(optstr); + return (-EINVAL); +} + +static int +make_secure(struct nfs_args *args, nfs_mnt_data_t *nmdp) +{ + sec_data_t *secdata; + + /* + * Check to see if any secure mode is requested. If not, use default + * security mode. Note: we currently only support sec=none and sec=sys. + */ + if (nmdp->nmd_sec_opt == 0) { + /* AUTH_UNIX is the default. */ + (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys", MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = nmdp->nmd_nfs_sec.sc_rpcnum = 1; + args->flags |= NFSMNT_SECDEFAULT; + } + + secdata = malloc(sizeof (sec_data_t)); + if (secdata == NULL) + return (-ENOMEM); + + (void) memset(secdata, 0, sizeof (sec_data_t)); + + secdata->secmod = nmdp->nmd_nfs_sec.sc_nfsnum; + secdata->rpcflavor = nmdp->nmd_nfs_sec.sc_rpcnum; + secdata->uid = nmdp->nmd_nfs_sec.sc_uid; + secdata->flags = 0; + secdata->data = NULL; + + args->nfs_args_ext = NFS_ARGS_EXTB; + args->nfs_ext_u.nfs_extB.secdata = secdata; + + return (0); +} + +/* + * Use our built-in netconfig table to lookup and construct a netconfig struct + * for the given netid. + */ +static struct netconfig * +get_netconf(char *id) +{ + int i; + struct netconfig *nconf, *np; + + if ((nconf = calloc(1, sizeof (struct netconfig))) == NULL) + return (NULL); + + for (i = 0; i < N_NETCONF_ENTS; i++) { + np = &nca[i]; + if (strcmp(np->nc_netid, id) != 0) + continue; + + nconf->nc_semantics = np->nc_semantics; + if ((nconf->nc_netid = strdup(np->nc_netid)) == NULL) + goto out; + if ((nconf->nc_protofmly = strdup(np->nc_protofmly)) == NULL) + goto out; + if ((nconf->nc_proto = strdup(np->nc_proto)) == NULL) + goto out; + if ((nconf->nc_device = strdup(np->nc_device)) == NULL) + goto out; + + return (nconf); + } + +out: + freenetconfigent(nconf); + return (NULL); +} + +/* + * If the user provided a logical name for the NFS server, then the user-level + * mount command will have already resolved that name and passed it in using + * the 'addr' option (see convert_nfs_arg_str where we've already handled this). + * We construct a netbuf from that provided IP and a given port option. + * + * Note: this code may need to be revisited when we add IPv6 support. + */ +static struct netbuf * +get_netbuf(struct netconfig *nconf, char *ip, ushort_t port) +{ + char buf[64]; + + assert(port != 0); + (void) snprintf(buf, sizeof (buf), "%s.%d.%d", ip, + port >> 8 & 0xff, port & 0xff); + return (uaddr2taddr(nconf, buf)); +} + +/* + * Construct a CLIENT handle to talk to the mountd without having to contact + * the rpcbind daemon on the server. This works for both the TCP and UDP cases, + * but it is primarily intended to the handle the TCP case. As an aside, note + * that TCP is never used by the native NFS mount client, even when the + * 'proto=tcp' argument is given to the mount command. The native mount code + * always uses UDP to get a file handle from the mountd. + */ +static CLIENT * +get_mountd_client(char *fshost, nfs_mnt_data_t *nmdp, int *fdp) +{ + struct netconfig *nconf; + struct netbuf *srvaddr; + CLIENT *cl = NULL; + rpcvers_t vers; + int fd; + struct t_bind *tbind = NULL; + struct t_info tinfo; + + vers = nmdp->nmd_mnt_vers; + *fdp = -1; + + if ((nconf = get_netconf(nmdp->nmd_mnt_proto)) == NULL) + return (NULL); + + if ((srvaddr = get_netbuf(nconf, fshost, nmdp->nmd_mnt_port)) == NULL) { + freenetconfigent(nconf); + return (NULL); + } + + tinfo.tsdu = 0; + if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) + goto done; + + /* LINTED pointer alignment */ + if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) { + (void) t_close(fd); + goto done; + } + + /* assign our srvaddr to tbind addr */ + (void) memcpy(tbind->addr.buf, srvaddr->buf, srvaddr->len); + tbind->addr.len = srvaddr->len; + + /* + * For compatibility, the mountd call to get the file handle must come + * from a privileged port. + */ + (void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL); + + cl = clnt_tli_create(fd, nconf, &tbind->addr, MOUNTPROG, vers, 0, 0); + if (cl == NULL) { + (void) t_close(fd); + /* + * Unfortunately, a failure in the: + * clnt_tli_create -> set_up_connection -> t_connect + * call path doesn't return any useful information about why we + * had an error. We basically see the following rpc_createerr + * status for a variety of conditions (e.g. invalid port, no + * service at IP, timeout, etc.): + * rpc_createerr.cf_stat == RPC_TLIERROR + * rpc_createerr.cf_error.re_terrno == 9 (TLOOK) + * rpc_createerr.cf_error.re_errno == 0 + */ + } else { + *fdp = fd; + } + +done: + if (tbind != NULL) + (void) t_free((char *)tbind, T_BIND); + free(srvaddr->buf); + free(srvaddr); + freenetconfigent(nconf); + + return (cl); +} + +static int +get_fh_cleanup(CLIENT *cl, int fd, int err) +{ + if (cl != NULL) + clnt_destroy(cl); + if (fd != -1) + (void) t_close(fd); + assert(err <= 0); + return (err); +} + +/* + * Get fhandle of remote path from server's mountd. This is only applicable to + * v2 or v3. + */ +static int +get_fh(struct nfs_args *args, char *fshost, char *fspath, nfs_mnt_data_t *nmdp) +{ + struct fhstatus fhs; + struct mountres3 mountres3; + struct pathcnf p; + nfs_fh3 *fh3p; + struct timeval timeout = { 25, 0}; + CLIENT *cl = NULL; + int fd = -1; + enum clnt_stat rpc_stat; + int count, i, *auths; + + bzero(&fhs, sizeof (fhs)); + bzero(&mountres3, sizeof (mountres3)); + bzero(&p, sizeof (p)); + + /* + * The user-level mount code should have contacted the server's rpcbind + * daemon and passed us the mount protocol and port. + */ + if (nmdp->nmd_mnt_port == 0 || nmdp->nmd_mnt_proto == NULL || + nmdp->nmd_mnt_vers == 0) { + return (-EAGAIN); + } + + cl = get_mountd_client(fshost, nmdp, &fd); + if (cl == NULL) { + /* + * As noted in get_mountd_client, we don't get a good indication + * as to why the connection failed. Linux returns ETIMEDOUT + * under many of the same conditions, and our native code notes + * that this is a common reason, so we do that here too. + */ + return (-ETIMEDOUT); + } + + if ((cl->cl_auth = authsys_create_default()) == NULL) { + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + + if (nmdp->nmd_mnt_vers == 2) { + rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath, + (caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout); + if (rpc_stat != RPC_SUCCESS) { + log_err("%s:%s: server not responding %s\n", + fshost, fspath, clnt_sperror(cl, "")); + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + + if ((errno = fhs.fhs_status) != MNT_OK) { + return (get_fh_cleanup(cl, fd, -fhs.fhs_status)); + } + args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle)); + if (args->fh == NULL) + return (get_fh_cleanup(cl, fd, -EAGAIN)); + + (void) memcpy((caddr_t)args->fh, + (caddr_t)&fhs.fhstatus_u.fhs_fhandle, + sizeof (fhs.fhstatus_u.fhs_fhandle)); + if (!errno && nmdp->nmd_posix) { + rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF, + xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf, + (caddr_t)&p, timeout); + if (rpc_stat != RPC_SUCCESS) { + log_err("%s:%s: server not responding %s\n", + fshost, fspath, clnt_sperror(cl, "")); + free(args->fh); + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + if (_PC_ISSET(_PC_ERROR, p.pc_mask)) { + free(args->fh); + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + args->flags |= NFSMNT_POSIX; + args->pathconf = malloc(sizeof (p)); + if (args->pathconf == NULL) { + free(args->fh); + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + (void) memcpy((caddr_t)args->pathconf, (caddr_t)&p, + sizeof (p)); + } + + } else { /* nmdp->nmd_mnt_vers == 3 */ + + rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath, + (caddr_t)&fspath, xdr_mountres3, (caddr_t)&mountres3, + timeout); + if (rpc_stat != RPC_SUCCESS) { + log_err("%s:%s: server not responding %s\n", + fshost, fspath, clnt_sperror(cl, "")); + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + + /* + * Assume here that most of the MNT3ERR_* + * codes map into E* errors. See the nfsstat enum for values. + */ + if ((errno = mountres3.fhs_status) != MNT_OK) { + return (get_fh_cleanup(cl, fd, -mountres3.fhs_status)); + } + + fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p)); + if (fh3p == NULL) + return (get_fh_cleanup(cl, fd, -EAGAIN)); + + fh3p->fh3_length = + mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len; + (void) memcpy(fh3p->fh3_u.data, + mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val, + fh3p->fh3_length); + args->fh = (caddr_t)fh3p; + nmdp->nmd_fstype = MNTTYPE_NFS3; + + /* + * If "sec=flavor" is a mount option, check if the server + * supports the "flavor". If the server does not support the + * flavor, return error. It is unlikely that the server will + * not support "sys", although "none" may not be allowed. + */ + auths = + mountres3.mountres3_u.mountinfo.auth_flavors + .auth_flavors_val; + count = + mountres3.mountres3_u.mountinfo.auth_flavors + .auth_flavors_len; + + if (count <= 0) { + return (get_fh_cleanup(cl, fd, -EAGAIN)); + } + + if (nmdp->nmd_sec_opt) { + for (i = 0; i < count; i++) { + if (auths[i] == nmdp->nmd_nfs_sec.sc_nfsnum) + break; + } + if (i == count) + return (get_fh_cleanup(cl, fd, -EACCES)); + } else { + /* AUTH_SYS is our default. */ + (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys", + MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = + nmdp->nmd_nfs_sec.sc_rpcnum = 1; + } + } + + return (get_fh_cleanup(cl, fd, 0)); +} + +/* + * Fill in the address for the server's NFS service and fill in a knetconfig + * structure for the transport that the service is available on. + */ +static int +getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp, + nfs_mnt_data_t *nmdp) +{ + struct stat sb; + struct netconfig *nconf = NULL; + struct knetconfig *knconfp; + struct t_info tinfo; + err_ret_t addr_error; + + SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0); + + /* + * Given the values passed in from the user-land mount command (or our + * built-in defaults), we should have the necessary NFS host address + * info. We have already validated that we have a supported + * nmd_nfs_proto. + */ + assert(nmdp->nmd_nfs_port != 0); + assert(nmdp->nmd_nfs_proto != NULL); + nconf = get_netconf(nmdp->nmd_nfs_proto); + assert(nconf != NULL); + args->addr = get_netbuf(nconf, fshost, nmdp->nmd_nfs_port); + if (args->addr == NULL) + return (-ENOMEM); + *nconfp = nconf; + tinfo.tsdu = 0; + + /* This shouldn't fail unless the zone is misconfigured */ + if (stat(nconf->nc_device, &sb) < 0) + return (-ENOSR); + + knconfp = (struct knetconfig *)malloc(sizeof (*knconfp)); + if (!knconfp) + return (-ENOMEM); + + knconfp->knc_semantics = nconf->nc_semantics; + knconfp->knc_protofmly = nconf->nc_protofmly; + knconfp->knc_proto = nconf->nc_proto; + knconfp->knc_rdev = sb.st_rdev; + args->flags |= NFSMNT_KNCONF; + args->knconf = knconfp; + + /* make sure we don't overload the transport */ + if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) { + args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE); + if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR) + args->rsize = tinfo.tsdu - NFS_RPC_HDR; + if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR) + args->wsize = tinfo.tsdu - NFS_RPC_HDR; + } + + return (0); +} + +static int +append_opt(char *optstr, int len, char *k, char *v) +{ + int i; + + for (i = 0; nmo_tab[i].nmo_lx_opt != NULL; i++) { + if (strcmp(k, nmo_tab[i].nmo_lx_opt) == 0) { + switch (nmo_tab[i].nmo_argtyp) { + case MOUNT_OPT_INVALID: + lx_unsupported("invalid NFS mount option: %s", + k); + return (-EINVAL); + + case MOUNT_OPT_PASTHRU: + if (*optstr != '\0') + (void) strlcat(optstr, ",", len); + if (v == NULL) { + (void) strlcat(optstr, k, len); + } else { + (void) strlcat(optstr, k, len); + (void) strlcat(optstr, "=", len); + (void) strlcat(optstr, v, len); + } + break; + + case MOUNT_OPT_IGNORE: + break; + + case MOUNT_OPT_TOKEN: + if (*optstr != '\0') + (void) strlcat(optstr, ",", len); + (void) strlcat(optstr, + nmo_tab[i].nmo_il_opt, len); + break; + + case MOUNT_OPT_HAS_ARG: + if (*optstr != '\0') + (void) strlcat(optstr, ",", len); + (void) strlcat(optstr, + nmo_tab[i].nmo_il_opt, len); + (void) strlcat(optstr, "=", len); + (void) strlcat(optstr, v, len); + break; + } + break; + } + } + + return (0); +} + +static int +get_nfs_kv(char *vs, char **kp, char **vp) +{ + char *p; + + p = strchr(vs, '='); + if (p == NULL) { + *kp = vs; + return (1); + } + + *vp = p + 1; + *p = '\0'; + *kp = vs; + return (0); +} + +/* + * Convert the Linux-specific opt string into an Illumos opt string. We also + * fix up the special string (host:/path) to use the address that the + * user-level mount code has looked up. This overwrites both the srcp special + * string and the mntopts string. + * + * example input string, given 'nolock' as the only user-level option: + * nolock,vers=4,addr=127.0.0.1,clientaddr=0.0.0.0 + * + * opt string (all one line) from a Centos 6 distro given 'nolock,vers=3' as + * the explicit options: + * nolock,addr=127.0.0.1,vers=3,proto=tcp,mountvers=3,mountproto=tcp, + * mountport=1892 + * + * This is an example emitted by the Ubuntu 14.04 automounter for an explicit + * v3 mount: + * timeo=60,soft,intr,sloppy,addr=10.88.88.200,vers=3,proto=tcp, + * mountvers=3,mountproto=tcp,mountport=63484 + */ +static int +convert_nfs_arg_str(char *srcp, char *mntopts, nfs_mnt_data_t *nmdp) +{ + char *key, *val, *p; + char tmpbuf[MAX_MNTOPT_STR]; + char *tbp = tmpbuf; + boolean_t no_sec = B_TRUE; + boolean_t no_addr = B_TRUE; + + (void) strlcpy(tmpbuf, mntopts, sizeof (tmpbuf)); + *mntopts = '\0'; + + while ((p = strsep(&tbp, ",")) != NULL) { + int tok; + + tok = get_nfs_kv(p, &key, &val); + + if (tok == 0) { + if (strcmp(key, "addr") == 0) { + /* + * The Linux user-level code looked up the + * address of the NFS server. We need to + * substitute that into the special string. + */ + char *pp; + char spec[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + + 1]; + + (void) strlcpy(spec, srcp, sizeof (spec)); + pp = strchr(spec, ':'); + if (pp == NULL) + return (-EINVAL); + + pp++; + (void) snprintf(srcp, + MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1, + "%s:%s", val, pp); + + no_addr = B_FALSE; + } else if (strcmp(key, "clientaddr") == 0) { + /* + * Ignore, this is an artifact of the + * user-level lx mount code. + */ + /* EMPTY */ + } else if (strcmp(key, "vers") == 0) { + /* + * This may be implicitly or explicitly passed. + * Check for the versions we want to support. + */ + int r; + int v = atoi(val); + + if (v != 3 && v != 4) + return (-EINVAL); + + r = append_opt(mntopts, MAX_MNTOPT_STR, + key, val); + if (r != 0) + return (r); + + } else if (strcmp(key, "sec") == 0) { + /* + * Linux supports: none, sys, krb5, krb5i, and + * krb5p. Of these, only none and sys overlap + * with our current support. Anything else is + * an error. + */ + int r; + + if (strcmp(val, "none") != 0 && + strcmp(val, "sys") != 0) + return (-EINVAL); + r = append_opt(mntopts, MAX_MNTOPT_STR, key, + val); + if (r != 0) + return (r); + no_sec = B_FALSE; + } else if (strcmp(key, "nolock") == 0) { + int r; + nmdp->nmd_nolock_opt = 1; + r = append_opt(mntopts, MAX_MNTOPT_STR, key, + val); + if (r != 0) + return (r); + } else { + int r; + + r = append_opt(mntopts, MAX_MNTOPT_STR, + key, val); + if (r != 0) + return (r); + } + } else { + int r; + + r = append_opt(mntopts, MAX_MNTOPT_STR, key, NULL); + if (r != 0) + return (r); + } + } + + if (no_addr) { + /* + * The Linux kernel requires an 'addr' option and will return + * EINVAL if one has not been provided. In particular, this + * behavior can be seen when the package which delivers NFS CLI + * support (e.g. nfs-common on Ubuntu, nfs-utils on Centos, + * etc.) is not installed. The generic mount command will not + * implicitly pass in the 'addr' option, the kernel will return + * EINVAL, and the mount will fail. + */ + return (-EINVAL); + } + + if (no_sec) { + /* + * XXX Temporarily work around missing DES auth by defaulting + * to sec=sys. + */ + int r; + + r = append_opt(mntopts, MAX_MNTOPT_STR, "sec", "sys"); + if (r != 0) + return (r); + } + + return (0); +} + +int +lx_nfs_mount(char *srcp, char *mntp, char *fst, int lx_flags, char *opts) +{ + int r; + int il_flags = 0; + nfs_mnt_data_t nmd, *nmdp = &nmd; + struct nfs_args *argp = NULL; + struct netconfig *nconf = NULL; + char *colonp; + char *path; + char *host; + char spec_buf[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1]; + + bzero(&nmd, sizeof (nmd)); + nmd.nmd_fstype = MNTTYPE_NFS; + + /* + * This will modify the special string so that the hostname passed + * in will be replaced with the host address that the user-land code + * looked up. This also converts the opts string so that we'll be + * dealing with illumos options after this. + */ + if ((r = convert_nfs_arg_str(srcp, opts, nmdp)) < 0) { + return (r); + } + + if (strcmp(fst, "nfs4") == 0) + nmdp->nmd_nfsvers = NFS_V4; + + /* Linux seems to always allow overlay mounts */ + il_flags |= MS_OVERLAY; + + /* Convert some Linux flags to Illumos flags. */ + if (lx_flags & LX_MS_RDONLY) + il_flags |= MS_RDONLY; + if (lx_flags & LX_MS_NOSUID) + il_flags |= MS_NOSUID; + if (lx_flags & LX_MS_REMOUNT) + il_flags |= MS_REMOUNT; + + /* + * Convert some Linux flags to Illumos option strings. + */ + if (lx_flags & LX_MS_STRICTATIME) { + /* + * The "strictatime" mount option ensures that none of the + * weaker atime-related mode options are in effect. + */ + lx_flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME); + } + if ((lx_flags & LX_MS_NODEV) && + ((r = i_add_option("nodev", opts, MAX_MNTOPT_STR)) != 0)) + return (r); + if ((lx_flags & LX_MS_NOEXEC) && + ((r = i_add_option("noexec", opts, MAX_MNTOPT_STR)) != 0)) + return (r); + if ((lx_flags & LX_MS_NOATIME) && + ((r = i_add_option("noatime", opts, MAX_MNTOPT_STR)) != 0)) + return (r); + + (void) strlcpy(spec_buf, srcp, sizeof (spec_buf)); + colonp = strchr(spec_buf, ':'); + if (colonp == NULL) + return (-EINVAL); + + *colonp = '\0'; + host = spec_buf; + path = colonp + 1; + + argp = (struct nfs_args *)malloc(sizeof (*argp)); + if (argp == NULL) + return (-ENOMEM); + + (void) memset(argp, 0, sizeof (*argp)); + (void) memset(&nmdp->nmd_nfs_sec, 0, sizeof (seconfig_t)); + nmdp->nmd_sec_opt = 0; + + /* returns a negative errno */ + if ((r = set_args(&il_flags, argp, host, opts, nmdp)) != 0) + goto out; + + if (nmdp->nmd_nfsvers == NFS_V4) { + /* + * In the case of version 4 there is no MOUNT program, thus no + * need for an RPC to get a file handle. + */ + nmdp->nmd_fstype = MNTTYPE_NFS4; + argp->fh = strdup(path); + if (argp->fh == NULL) { + r = -ENOMEM; + goto out; + } + } else { + if ((r = get_fh(argp, host, path, nmdp)) < 0) + goto out; + } + + if ((r = getaddr_nfs(argp, host, &nconf, nmdp)) < 0) + goto out; + + if ((r = make_secure(argp, nmdp)) < 0) + goto out; + + il_flags |= MS_DATA | MS_OPTIONSTR; + + r = mount(srcp, mntp, il_flags, nmdp->nmd_fstype, argp, sizeof (*argp), + opts, MAX_MNTOPT_STR); + if (r != 0) { + r = -errno; + } else if (nmdp->nmd_nolock_opt == 0) { + (void) syscall(SYS_brand, B_START_NFS_LOCKD); + } + +out: + if (nconf != NULL) + freenetconfigent(nconf); + if (argp->fh) + free(argp->fh); + if (argp->pathconf) + free(argp->pathconf); + if (argp->knconf) + free(argp->knconf); + if (argp->addr) { + free(argp->addr->buf); + free(argp->addr); + } + if (argp->nfs_ext_u.nfs_extB.secdata) + free(argp->nfs_ext_u.nfs_extB.secdata); + if (argp->syncaddr) { + free(argp->syncaddr->buf); + free(argp->syncaddr); + } + if (argp->netname) + free(argp->netname); + free(argp); + if (nmdp->nmd_nfs_proto != NULL) + free(nmdp->nmd_nfs_proto); + + return (r); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/ptrace.c b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c new file mode 100644 index 0000000000..2c6f5041a1 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c @@ -0,0 +1,105 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> +#include <sys/lx_syscall.h> +#include <sys/lx_signal.h> +#include <sys/lx_thread.h> +#include <sys/lwp.h> +#include <unistd.h> +#include <fcntl.h> +#include <procfs.h> +#include <sys/frame.h> +#include <strings.h> +#include <signal.h> +#include <stddef.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <sys/auxv.h> +#include <thread.h> +#include <pthread.h> +#include <synch.h> +#include <elf.h> +#include <ieeefp.h> +#include <assert.h> +#include <libintl.h> +#include <lx_syscall.h> + +/* + * Much of the Linux ptrace(2) emulation is performed in the kernel, and there + * is a block comment in "lx_ptrace.c" that describes the facility in some + * detail. + */ + + +void +lx_ptrace_stop_if_option(int option, boolean_t child, ulong_t msg, + ucontext_t *ucp) +{ + /* + * We call into the kernel to see if we need to stop for specific + * ptrace(2) events. + */ + lx_debug("lx_ptrace_stop_if_option(%d, %s, %lu, %p)", option, + child ? "TRUE [child]" : "FALSE [parent]", msg, ucp); + if (ucp == NULL) { + ucp = (ucontext_t *)lx_find_brand_uc(); + lx_debug("\tucp = %p", ucp); + } + if (syscall(SYS_brand, B_PTRACE_STOP_FOR_OPT, option, child, msg, + ucp) != 0) { + if (errno != ESRCH) { + /* + * This should _only_ fail if we are not traced, or do + * not have this option set. + */ + lx_err_fatal("B_PTRACE_STOP_FOR_OPT failed: %s", + strerror(errno)); + } + } +} + +/* + * Signal to the in-kernel ptrace(2) subsystem that the next native fork() or + * thr_create() is part of an emulated fork(2) or clone(2). If PTRACE_CLONE + * was passed to clone(2), inherit_flag should be B_TRUE. + */ +void +lx_ptrace_clone_begin(int option, boolean_t inherit_flag, int flags) +{ + lx_debug("lx_ptrace_clone_begin(%d, %sPTRACE_CLONE)", option, + inherit_flag ? "" : "!"); + if (syscall(SYS_brand, B_PTRACE_CLONE_BEGIN, option, + inherit_flag, flags) != 0) { + lx_err_fatal("B_PTRACE_CLONE_BEGIN failed: %s", + strerror(errno)); + } +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/sendfile.c b/usr/src/lib/brand/lx/lx_brand/common/sendfile.c new file mode 100644 index 0000000000..c09e8c51dc --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/sendfile.c @@ -0,0 +1,143 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. + */ + +/* + * lx_sendfile() and lx_sendfile64() are primarily branded versions of the + * library calls available in the Solaris libsendfile (see sendfile(3EXT)). + */ + +#include <sys/types.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/sendfile.h> +#include <string.h> +#include <errno.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +#if defined(_ILP32) +long +lx_sendfile(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + sysret_t rval; + off_t off = 0; + off_t *offp = (off_t *)p3; + int error; + struct sendfilevec sfv; + size_t xferred = 0; + size_t sz = (size_t)p4; + + if (offp == NULL) { + /* If no offp, we must use the current offset */ + if ((off = lseek((int)p2, 0, SEEK_CUR)) == -1) + return (-errno); + } else { + if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0) + return (-errno); + if (off < 0) + return (-EINVAL); + } + + sfv.sfv_fd = p2; + sfv.sfv_flag = 0; + sfv.sfv_off = off; + sfv.sfv_len = sz; + error = __systemcall(&rval, SYS_sendfilev, SENDFILEV, p1, &sfv, + 1, &xferred); + + /* Suppress errors if we were able to write any data at all. */ + if (xferred > 0) { + error = 0; + } + + if (error == 0) { + off += xferred; + if (offp == NULL) { + /* If no offp, we must adjust current offset */ + if (lseek((int)p2, off, SEEK_SET) == -1) + return (-errno); + } else { + if (uucopy(&off, offp, sizeof (off)) != 0) { + return (-EFAULT); + } + } + } + + return (error ? -error : xferred); +} +#endif + +long +lx_sendfile64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + sysret_t rval; + off64_t off = 0; + off64_t *offp = (off64_t *)p3; + size_t sz = (size_t)p4; + int error; + struct sendfilevec64 sfv; + size_t xferred; + + if (offp == NULL) { + /* If no offp, we must use the current offset */ + if ((off = lseek((int)p2, 0, SEEK_CUR)) == -1) + return (-errno); + } else { + if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0) + return (-errno); + if (off < 0) + return (-EINVAL); + } + + sfv.sfv_fd = p2; + sfv.sfv_flag = 0; + sfv.sfv_off = off; + sfv.sfv_len = sz; + xferred = 0; + error = __systemcall(&rval, SYS_sendfilev, SENDFILEV64, p1, &sfv, + 1, &xferred); + + /* Suppress errors if we were able to write any data at all. */ + if (xferred > 0) { + error = 0; + } + + if (error == 0) { + off += xferred; + if (offp == NULL) { + /* If no offp, we must adjust current offset */ + if (lseek((int)p2, off, SEEK_SET) == -1) + return (-errno); + } else { + if (uucopy(&off, offp, sizeof (off)) != 0) { + return (-EFAULT); + } + } + } + + return (error ? -error : xferred); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/signal.c b/usr/src/lib/brand/lx/lx_brand/common/signal.c new file mode 100644 index 0000000000..74abff9c8c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/signal.c @@ -0,0 +1,2419 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/segments.h> +#include <sys/lx_types.h> +#include <sys/lx_brand.h> +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> +#include <sys/lx_poll.h> +#include <sys/lx_signal.h> +#include <sys/lx_sigstack.h> +#include <sys/lx_syscall.h> +#include <sys/lx_thread.h> +#include <sys/lx_userhz.h> +#include <sys/syscall.h> +#include <lx_provider_impl.h> +#include <sys/stack.h> +#include <assert.h> +#include <errno.h> +#include <poll.h> +#include <rctl.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <thread.h> +#include <ucontext.h> +#include <unistd.h> +#include <stdio.h> +#include <libintl.h> +#include <ieeefp.h> +#include <sys/signalfd.h> + +#if defined(_ILP32) +extern int pselect_large_fdset(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0, + const timespec_t *tsp, const sigset_t *sp); +#endif + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +/* + * Delivering signals to a Linux process is complicated by differences in + * signal numbering, stack structure and contents, and the action taken when a + * signal handler exits. In addition, many signal-related structures, such as + * sigset_ts, vary between Illumos and Linux. + * + * To support user-level signal handlers, the brand uses a double layer of + * indirection to process and deliver signals to branded threads. + * + * When a Linux process sends a signal using the kill(2) system call, we must + * translate the signal into the Illumos equivalent before handing control off + * to the standard signalling mechanism. When a signal is delivered to a Linux + * process, we translate the signal number from Illumos to back to Linux. + * Translating signals both at generation and delivery time ensures both that + * Illumos signals are sent properly to Linux applications and that signals' + * default behavior works as expected. + * + * In a normal Illumos process, signal delivery is interposed on for any thread + * registering a signal handler by libc. Libc needs to do various bits of magic + * to provide thread-safe critical regions, so it registers its own handler, + * named sigacthandler(), using the sigaction(2) system call. When a signal is + * received, sigacthandler() is called, and after some processing, libc turns + * around and calls the user's signal handler via a routine named + * call_user_handler(). + * + * Adding a Linux branded thread to the mix complicates things somewhat. + * + * First, when a thread receives a signal, it may either be running in an + * emulated Linux context or a native illumos context. In either case, the + * in-kernel brand module is responsible for preserving the register state + * from the interrupted context, regardless of whether emulated or native + * software was running at the time. The kernel is also responsible for + * ensuring that the illumos native sigacthandler() is called with register + * values appropriate for native code. Of particular note is the %gs segment + * selector for 32-bit code, and the %fsbase segment base register for 64-bit + * code; these are used by libc to locate per-thread data structures. + * + * Second, the signal number translation referenced above must take place. + * Finally, when we hand control to the Linux signal handler we must do so + * on the brand stack, and with registers configured appropriately for the + * Linux application. + * + * This need to translate signal numbers (and manipulate the signal handling + * context) means that with standard Illumos libc, following a signal from + * generation to delivery looks something like: + * + * kernel -> + * sigacthandler() -> + * call_user_handler() -> + * user signal handler + * + * but for the brand's Linux threads, this would look like: + * + * kernel -> + * sigacthandler() -> + * call_user_handler() -> + * lx_call_user_handler() -> + * lx_sigdeliver() -> + * syscall(B_JUMP_TO_LINUX, ...) -> + * Linux user signal handler + * + * The new addtions are: + * + * lx_call_user_handler + * ==================== + * This routine is responsible for translating Illumos signal numbers to + * their Linux equivalents, building a Linux signal stack based on the + * information Illumos has provided, and passing the stack to the + * registered Linux signal handler. It is, in effect, the Linux thread + * equivalent to libc's call_user_handler(). + * + * lx_sigdeliver + * ============= + * + * Note that none of this interposition is necessary unless a Linux thread + * registers a user signal handler, as the default action for all signals is the + * same between Illumos and Linux save for one signal, SIGPWR. For this reason, + * the brand ALWAYS installs its own internal signal handler for SIGPWR that + * translates the action to the Linux default, to terminate the process. + * (Illumos' default action is to ignore SIGPWR.) + * + * A notable behavior of lx_sigdeliver is that it must replace the stack + * pointer in the context that will be handed to the Linux signal handler. + * There is at least one application (mono) which inspects the SP in the + * context it receives and which fails when the SP is not within the thread's + * stack range. There is not much else within the context that a signal + * handler could depend on, so we only ensure that the SP is from the Linux + * stack and not the alternate stack. lx_sigdeliver will restore the correct + * SP when setcontext returns into this function as part of returning from + * the signal handler. + * + * It is also important to note that when signals are not translated, the brand + * relies upon code interposing upon the wait(2) system call to translate + * signals to their proper values for any Linux threads retrieving the status + * of others. So while the Illumos signal number for a particular signal is set + * in a process' data structures (and would be returned as the result of say, + * WTERMSIG()), the brand's interposiiton upon wait(2) is responsible for + * translating the value WTERMSIG() would return from a Illumos signal number + * to the appropriate Linux value. + * + * lx_call_user_handler() calls lx_sigdeliver() with a helper function + * (typically lx_build_signal_frame) which builds a stack frame for the 32-bit + * Linux signal handler, or populates a local (on the stack) structure for the + * 64-bit Linux signal handler. The stack at that time looks like this: + * + * ========================================================= + * | | lx_sigdeliver_frame_t -- includes LX_SIGRT_MAGIC and | + * | | a return context for the eventual sigreturn(2) call | + * | ========================================================= + * | | Linux signal frame (32-bit) or local data | + * V | (64-bit) built by stack_builder() | + * ========================================================= + * + * The process of returning to an interrupted thread of execution from a user + * signal handler is entirely different between Illumos and Linux. While + * Illumos generally expects to set the context to the interrupted one on a + * normal return from a signal handler, in the normal case Linux instead calls + * code that calls a specific Linux system call, rt_sigreturn(2) (or it also + * can call sigreturn(2) in 32-bit code). Thus when a Linux signal handler + * completes execution, instead of returning through what would in libc be a + * call to setcontext(2), the rt_sigreturn(2) Linux system call is responsible + * for accomplishing much the same thing. It's for this reason that the stack + * frame we build has the lx_(rt_)sigreturn_tramp code on the top of the + * stack. The code looks like this: + * + * 32-bit 64-bit + * -------------------------------- ----------------------------- + * mov LX_SYS_rt_sigreturn, %eax movq LX_SYS_rt_sigreturn, %rax + * int $0x80 syscall + * + * We also use these same functions (lx_rt_sigreturn_tramp or + * lx_sigreturn_tramp) to actually return from the signal handler. + * + * (Note that this trampoline code actually lives in a proper executable segment + * and not on the stack, but gdb checks for the exact code sequence of the + * trampoline code on the stack to determine whether it is in a signal stack + * frame or not. Really.) + * + * When the 32-bit Linux user signal handler is eventually called, the brand + * stack frame looks like this (in the case of a "modern" signal stack; see + * the lx_sigstack structure definition): + * + * ========================================================= + * | | lx_sigdeliver_frame_t | + * | ========================================================= + * | | Trampoline code (marker for gdb, not really executed) | + * | ========================================================= + * | | Linux struct _fpstate | + * | ========================================================= + * V | Linux ucontext_t | <--+ + * ========================================================= | + * | Linux siginfo_t | <--|-----+ + * ========================================================= | | + * | Pointer to Linux ucontext_t (or NULL) (sigaction arg2)| ---+ | + * ========================================================= | + * | Pointer to Linux siginfo_t (or NULL) (sigaction arg1)| ---------+ + * ========================================================= + * | Linux signal number (sigaction arg0)| + * ========================================================= + * | Pointer to signal return code (trampoline code) | + * ========================================================= + * + * The 64-bit stack-local data looks like this: + * + * ========================================================= + * | | lx_sigdeliver_frame_t | + * | ========================================================= + * | | Trampoline code (marker for gdb, not really executed) | + * | ========================================================= + * | | Linux struct _fpstate | + * | ========================================================= + * V | Linux ucontext_t | %rdx arg2 + * ========================================================= + * | Linux siginfo_t | %rsi arg1 + * ========================================================= + * | Pointer to signal return code (trampoline code) | + * ========================================================= + * + * As usual in 64-bit code, %rdi is arg0 which is the signal number. + * + * The *sigreturn(2) family of emulated system call handlers locates the + * "lx_sigdeliver_frame_t" struct on the Linux stack as part of processing + * the system call. This object contains a guard value (LX_SIGRT_MAGIC) to + * detect stack smashing or an incorrect stack pointer. It also contains a + * "return" context, which we use to get back to the "lx_sigdeliver()" frame + * on the native stack that originally dispatched to the Linux signal + * handler. The lx_sigdeliver() function is then able to return to the + * native libc signal handler in the usual way. This results in a further + * setcontext() back to whatever was running when we took the signal. + * + * There are some edge cases where the "return" context cannot be located + * by inspection of the Linux stack; e.g. if the guard value has been + * corrupted, or the emulated program has relocated parts of the signal + * delivery stack frame. If this case is detected, a fallback mechanism is + * used to attempt to find the return context. A chain of "lx_sigbackup_t" + * objects is maintained in signal interposer call frames, with the current + * head stored in the thread-specific "lx_tsd_t". This mechanism is + * similar in principle to the "lwp_oldcontext" member of the "klwp_t" used + * by the native signal handling infrastructure. This backup chain is used + * by the sigreturn(2) family of emulated system calls in the event that + * the Linux stack did not correctly reference a return context. + */ + +typedef struct lx_sigdeliver_frame { + uintptr_t lxsdf_magic; + ucontext_t *lxsdf_retucp; + ucontext_t *lxsdf_sigucp; + lx_sigbackup_t *lxsdf_sigbackup; +} lx_sigdeliver_frame_t; + +struct lx_oldsigstack { + void (*retaddr)(); /* address of real lx_sigreturn code */ + int sig; /* signal number */ + lx_sigcontext_t sigc; /* saved user context */ + lx_fpstate_t fpstate; /* saved FP state */ + int sig_extra; /* signal mask for signals [32 .. NSIG - 1] */ + char trampoline[8]; /* code for trampoline to lx_sigreturn() */ +}; + +/* + * The lx_sighandlers structure needs to be a global due to the semantics of + * clone(). + * + * If CLONE_SIGHAND is set, the calling process and child share signal + * handlers, and if either calls sigaction(2) it should change the behavior + * in the other thread. Each thread does, however, have its own signal mask + * and set of pending signals. + * + * If CLONE_SIGHAND is not set, the child process should inherit a copy of + * the signal handlers at the time of the clone() but later calls to + * sigaction(2) should only affect the individual thread calling it. + * + * This maps perfectly to a thr_create(3C) thread semantic in the first + * case and a fork(2)-type semantic in the second case. By making + * lx_sighandlers global, we automatically get the correct behavior. + */ +static lx_sighandlers_t lx_sighandlers; + +static void lx_sigdeliver(int, siginfo_t *, ucontext_t *, size_t, void (*)(), + void (*)(), struct lx_sigaction *); + +/* + * stol_stack() and ltos_stack() convert between Illumos and Linux stack_t + * structures. + * + * These routines are needed because although the two structures have the same + * contents, their contents are declared in a different order, so the content + * of the structures cannot be copied with a simple bcopy(). + */ +static void +stol_stack(stack_t *fr, lx_stack_t *to) +{ + to->ss_sp = fr->ss_sp; + to->ss_flags = fr->ss_flags; + to->ss_size = fr->ss_size; +} + +static void +ltos_stack(lx_stack_t *fr, stack_t *to) +{ + to->ss_sp = fr->ss_sp; + to->ss_flags = fr->ss_flags; + to->ss_size = fr->ss_size; +} + +static int +ltos_sigset(lx_sigset_t *lx_sigsetp, sigset_t *s_sigsetp) +{ + lx_sigset_t l; + int lx_sig, sig; + + if (uucopy(lx_sigsetp, &l, sizeof (lx_sigset_t)) != 0) + return (-errno); + + (void) sigemptyset(s_sigsetp); + + for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) { + if (lx_sigismember(&l, lx_sig) && + ((sig = ltos_signo[lx_sig]) > 0)) + (void) sigaddset(s_sigsetp, sig); + } + + return (0); +} + +static int +stol_sigset(sigset_t *s_sigsetp, lx_sigset_t *lx_sigsetp) +{ + lx_sigset_t l; + int sig, lx_sig; + + bzero(&l, sizeof (lx_sigset_t)); + + for (sig = 1; sig < NSIG; sig++) { + if (sigismember(s_sigsetp, sig) && + ((lx_sig = stol_signo[sig]) > 0)) + lx_sigaddset(&l, lx_sig); + } + + return ((uucopy(&l, lx_sigsetp, sizeof (lx_sigset_t)) != 0) + ? -errno : 0); +} + +#if defined(_ILP32) +static int +ltos_osigset(lx_osigset_t *lx_osigsetp, sigset_t *s_sigsetp) +{ + lx_osigset_t lo; + int lx_sig, sig; + + if (uucopy(lx_osigsetp, &lo, sizeof (lx_osigset_t)) != 0) + return (-errno); + + (void) sigemptyset(s_sigsetp); + + for (lx_sig = 1; lx_sig <= OSIGSET_NBITS; lx_sig++) + if ((lo & OSIGSET_BITSET(lx_sig)) && + ((sig = ltos_signo[lx_sig]) > 0)) + (void) sigaddset(s_sigsetp, sig); + + return (0); +} + +static int +stol_osigset(sigset_t *s_sigsetp, lx_osigset_t *lx_osigsetp) +{ + lx_osigset_t lo = 0; + int lx_sig, sig; + + /* + * Note that an lx_osigset_t can only represent the signals from + * [1 .. OSIGSET_NBITS], so even though a signal may be present in the + * Illumos sigset_t, it may not be representable as a bit in the + * lx_osigset_t. + */ + for (sig = 1; sig < NSIG; sig++) + if (sigismember(s_sigsetp, sig) && + ((lx_sig = stol_signo[sig]) > 0) && + (lx_sig <= OSIGSET_NBITS)) + lo |= OSIGSET_BITSET(lx_sig); + + return ((uucopy(&lo, lx_osigsetp, sizeof (lx_osigset_t)) != 0) + ? -errno : 0); +} +#endif + +static int +ltos_sigcode(int si_code) +{ + switch (si_code) { + case LX_SI_USER: + return (SI_USER); + case LX_SI_TKILL: + return (SI_LWP); + case LX_SI_QUEUE: + return (SI_QUEUE); + case LX_SI_TIMER: + return (SI_TIMER); + case LX_SI_ASYNCIO: + return (SI_ASYNCIO); + case LX_SI_MESGQ: + return (SI_MESGQ); + default: + return (LX_SI_CODE_NOT_EXIST); + } +} + +int +stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop) +{ + int ret = 0; + lx_siginfo_t lx_siginfo; + + bzero(&lx_siginfo, sizeof (*lx_siginfop)); + + if ((lx_siginfo.lsi_signo = stol_signo[siginfop->si_signo]) <= 0) { + /* + * Depending on the caller we may still need to get a usable + * converted siginfo struct. + */ + lx_siginfo.lsi_signo = LX_SIGKILL; + errno = EINVAL; + ret = -1; + } + + lx_siginfo.lsi_code = lx_stol_sigcode(siginfop->si_code); + lx_siginfo.lsi_errno = siginfop->si_errno; + + switch (lx_siginfo.lsi_signo) { + /* + * Semantics ARE defined for SIGKILL, but since + * we can't catch it, we can't translate it. :-( + */ + case LX_SIGPOLL: + lx_siginfo.lsi_band = siginfop->si_band; + lx_siginfo.lsi_fd = siginfop->si_fd; + break; + + case LX_SIGCHLD: + lx_siginfo.lsi_pid = siginfop->si_pid; + if (siginfop->si_code <= 0 || siginfop->si_code == + CLD_EXITED) { + lx_siginfo.lsi_status = siginfop->si_status; + } else { + lx_siginfo.lsi_status = lx_stol_status( + siginfop->si_status, -1); + } + lx_siginfo.lsi_utime = + HZ_TO_LX_USERHZ(siginfop->si_utime); + lx_siginfo.lsi_stime = + HZ_TO_LX_USERHZ(siginfop->si_stime); + break; + + case LX_SIGILL: + case LX_SIGBUS: + case LX_SIGFPE: + case LX_SIGSEGV: + lx_siginfo.lsi_addr = siginfop->si_addr; + break; + + default: + lx_siginfo.lsi_pid = siginfop->si_pid; + lx_siginfo.lsi_uid = + LX_UID32_TO_UID16(siginfop->si_uid); + lx_siginfo.lsi_value = siginfop->si_value; + break; + } + + if (uucopy(&lx_siginfo, lx_siginfop, sizeof (lx_siginfo_t)) != 0) + return (-errno); + return ((ret != 0) ? -errno : 0); +} + +static void +stol_fpstate(fpregset_t *fpr, lx_fpstate_t *lfpr) +{ + size_t copy_len; + +#if defined(_LP64) + /* + * The 64-bit Illumos struct fpregset_t and lx_fpstate_t are identical + * so just bcopy() those entries (see usr/src/uts/intel/sys/regset.h + * for __amd64's struct fpu). + */ + copy_len = sizeof (fpr->fp_reg_set.fpchip_state); + bcopy(fpr, lfpr, copy_len); + +#else /* is _ILP32 */ + struct _fpstate *fpsp = (struct _fpstate *)fpr; + + /* + * The Illumos struct _fpstate and lx_fpstate_t are identical from the + * beginning of the structure to the lx_fpstate_t "magic" field, so + * just bcopy() those entries. + */ + copy_len = (size_t)&(((lx_fpstate_t *)0)->magic); + bcopy(fpsp, lfpr, copy_len); + + /* + * These fields are all only significant for the first 16 bits. + */ + lfpr->cw &= 0xffff; /* x87 control word */ + lfpr->tag &= 0xffff; /* x87 tag word */ + lfpr->cssel &= 0xffff; /* cs selector */ + lfpr->datasel &= 0xffff; /* ds selector */ + + /* + * Linux wants the x87 status word field to contain the value of the + * x87 saved exception status word. + */ + lfpr->sw = lfpr->status & 0xffff; /* x87 status word */ + + lfpr->mxcsr = fpsp->mxcsr; + + if (fpsp->mxcsr != 0) { + /* + * Linux uses the "magic" field to denote whether the XMM + * registers contain legal data or not. Since we can't get to + * %cr4 from userland to check the status of the OSFXSR bit, + * check the mxcsr field to see if it's 0, which it should + * never be on a system with the OXFXSR bit enabled. + */ + lfpr->magic = LX_X86_FXSR_MAGIC; + bcopy(fpsp->xmm, lfpr->_xmm, sizeof (lfpr->_xmm)); + } else { + lfpr->magic = LX_X86_FXSR_NONE; + } +#endif +} + +static void +ltos_fpstate(lx_fpstate_t *lfpr, fpregset_t *fpr) +{ + size_t copy_len; + +#if defined(_LP64) + /* + * The 64-bit Illumos struct fpregset_t and lx_fpstate_t are identical + * so just bcopy() those entries (see usr/src/uts/intel/sys/regset.h + * for __amd64's struct fpu). + */ + copy_len = sizeof (fpr->fp_reg_set.fpchip_state); + bcopy(lfpr, fpr, copy_len); + +#else /* is _ILP32 */ + struct _fpstate *fpsp = (struct _fpstate *)fpr; + + /* + * The lx_fpstate_t and Illumos struct _fpstate are identical from the + * beginning of the structure to the struct _fpstate "mxcsr" field, so + * just bcopy() those entries. + * + * Note that we do NOT have to propogate changes the user may have made + * to the "status" word back to the "sw" word, unlike the way we have + * to deal with processing the ESP and UESP register values on return + * from a signal handler. + */ + copy_len = (size_t)&(((struct _fpstate *)0)->mxcsr); + bcopy(lfpr, fpsp, copy_len); + + /* + * These fields are all only significant for the first 16 bits. + */ + fpsp->cw &= 0xffff; /* x87 control word */ + fpsp->sw &= 0xffff; /* x87 status word */ + fpsp->tag &= 0xffff; /* x87 tag word */ + fpsp->cssel &= 0xffff; /* cs selector */ + fpsp->datasel &= 0xffff; /* ds selector */ + fpsp->status &= 0xffff; /* saved status */ + + fpsp->mxcsr = lfpr->mxcsr; + + if (lfpr->magic == LX_X86_FXSR_MAGIC) + bcopy(lfpr->_xmm, fpsp->xmm, sizeof (fpsp->xmm)); +#endif +} + +/* + * We do not use the system sigaltstack() infrastructure as that would conflict + * with our handling of both system call emulation and native signals on the + * native stack. Instead, we track the Linux stack structure in our + * thread-specific data. This function is modeled on the behaviour of the + * native sigaltstack system call handler. + */ +long +lx_sigaltstack(uintptr_t ssp, uintptr_t oss) +{ + lx_tsd_t *lxtsd = lx_get_tsd(); + lx_stack_t ss; + + if (ssp != (uintptr_t)NULL) { + if (lxtsd->lxtsd_sigaltstack.ss_flags & LX_SS_ONSTACK) { + /* + * If we are currently using the installed alternate + * stack for signal handling, the user may not modify + * the stack for this thread. + */ + return (-EPERM); + } + + if (uucopy((void *)ssp, &ss, sizeof (ss)) != 0) { + return (-EFAULT); + } + + if (ss.ss_flags & ~LX_SS_DISABLE) { + /* + * The user may not specify a value for flags other + * than 0 or SS_DISABLE. + */ + return (-EINVAL); + } + + if (!(ss.ss_flags & LX_SS_DISABLE) && ss.ss_size < + LX_MINSIGSTKSZ) { + return (-ENOMEM); + } + + if ((ss.ss_flags & LX_SS_DISABLE) != 0) { + ss.ss_sp = NULL; + ss.ss_size = 0; + } + } + + if (oss != (uintptr_t)NULL) { + /* + * User provided old and new stack_t pointers may point to + * the same location. Copy out before we modify. + */ + if (uucopy(&lxtsd->lxtsd_sigaltstack, (void *)oss, + sizeof (lxtsd->lxtsd_sigaltstack)) != 0) { + return (-EFAULT); + } + } + + if (ssp != (uintptr_t)NULL) { + lxtsd->lxtsd_sigaltstack = ss; + } + + return (0); +} + +#if defined(_ILP32) +/* + * The following routines are needed because sigset_ts and siginfo_ts are + * different in format between Linux and Illumos. + * + * Note that there are two different lx_sigset structures, lx_sigset_ts and + * lx_osigset_ts: + * + * + An lx_sigset_t is the equivalent of a Illumos sigset_t and supports + * more than 32 signals. + * + * + An lx_osigset_t is simply a uint32_t, so it by definition only supports + * 32 signals. + * + * When there are two versions of a routine, one prefixed with lx_rt_ and + * one prefixed with lx_ alone, in GENERAL the lx_rt_ routines deal with + * lx_sigset_ts while the lx_ routines deal with lx_osigset_ts. Unfortunately, + * this is not always the case (e.g. lx_sigreturn() vs. lx_rt_sigreturn()) + */ +long +lx_sigpending(uintptr_t sigpend) +{ + sigset_t sigpendset; + + if (sigpending(&sigpendset) != 0) + return (-errno); + + return (stol_osigset(&sigpendset, (lx_osigset_t *)sigpend)); +} +#endif + +long +lx_rt_sigpending(uintptr_t sigpend, uintptr_t setsize) +{ + sigset_t sigpendset; + + if ((size_t)setsize != sizeof (lx_sigset_t)) + return (-EINVAL); + + if (sigpending(&sigpendset) != 0) + return (-errno); + + return (stol_sigset(&sigpendset, (lx_sigset_t *)sigpend)); +} + +/* + * Create a common routine to encapsulate all of the sigprocmask code, + * as the only difference between lx_sigprocmask() and lx_rt_sigprocmask() + * is the usage of lx_osigset_ts vs. lx_sigset_ts, as toggled in the code by + * the setting of the "sigset_type" flag. + */ +static int +lx_sigprocmask_common(uintptr_t how, uintptr_t l_setp, uintptr_t l_osetp, + uintptr_t sigset_type) +{ + int err = 0; + sigset_t set, oset; + sigset_t *s_setp = NULL; + sigset_t *s_osetp; + + if (l_setp) { + switch (how) { + case LX_SIG_BLOCK: + how = SIG_BLOCK; + break; + + case LX_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + + case LX_SIG_SETMASK: + how = SIG_SETMASK; + break; + + default: + return (-EINVAL); + } + + s_setp = &set; + + /* Only 32-bit code passes other than USE_SIGSET */ + if (sigset_type == USE_SIGSET) + err = ltos_sigset((lx_sigset_t *)l_setp, s_setp); +#if defined(_ILP32) + else + err = ltos_osigset((lx_osigset_t *)l_setp, s_setp); +#endif + + if (err != 0) + return (err); + + } + + s_osetp = (l_osetp ? &oset : NULL); + + /* + * In a multithreaded environment, a call to sigprocmask(2) should + * only affect the current thread's signal mask so we don't need to + * explicitly call thr_sigsetmask(3C) here. + */ + if (sigprocmask(how, s_setp, s_osetp) != 0) + return (-errno); + + if (l_osetp) { + if (sigset_type == USE_SIGSET) + err = stol_sigset(s_osetp, (lx_sigset_t *)l_osetp); +#if defined(_ILP32) + else + err = stol_osigset(s_osetp, (lx_osigset_t *)l_osetp); +#endif + + if (err != 0) { + /* + * Encountered a fault while writing to the old signal + * mask buffer, so unwind the signal mask change made + * above. + */ + (void) sigprocmask(how, s_osetp, (sigset_t *)NULL); + return (err); + } + } + + return (0); +} + +#if defined(_ILP32) +long +lx_sigprocmask(uintptr_t how, uintptr_t setp, uintptr_t osetp) +{ + return (lx_sigprocmask_common(how, setp, osetp, USE_OSIGSET)); +} +#endif + +long +lx_rt_sigprocmask(uintptr_t how, uintptr_t setp, uintptr_t osetp, + uintptr_t setsize) +{ + if ((size_t)setsize != sizeof (lx_sigset_t)) + return (-EINVAL); + + return (lx_sigprocmask_common(how, setp, osetp, USE_SIGSET)); +} + +#if defined(_ILP32) +long +lx_sigsuspend(uintptr_t set) +{ + sigset_t s_set; + + if (ltos_osigset((lx_osigset_t *)set, &s_set) != 0) + return (-errno); + + return ((sigsuspend(&s_set) == -1) ? -errno : 0); +} +#endif + +long +lx_rt_sigsuspend(uintptr_t set, uintptr_t setsize) +{ + sigset_t s_set; + + if ((size_t)setsize != sizeof (lx_sigset_t)) + return (-EINVAL); + + if (ltos_sigset((lx_sigset_t *)set, &s_set) != 0) + return (-errno); + + return ((sigsuspend(&s_set) == -1) ? -errno : 0); +} + +long +lx_rt_sigwaitinfo(uintptr_t set, uintptr_t sinfo, uintptr_t setsize) +{ + sigset_t s_set; + siginfo_t s_sinfo, *s_sinfop; + int rc; + + lx_sigset_t *setp = (lx_sigset_t *)set; + lx_siginfo_t *sinfop = (lx_siginfo_t *)sinfo; + + if ((size_t)setsize != sizeof (lx_sigset_t)) + return (-EINVAL); + + if (ltos_sigset(setp, &s_set) != 0) + return (-errno); + + s_sinfop = (sinfop == NULL) ? NULL : &s_sinfo; + + if ((rc = sigwaitinfo(&s_set, s_sinfop)) == -1) + return (-errno); + + if (s_sinfop == NULL) + return (stol_signo[rc]); + + return ((stol_siginfo(s_sinfop, sinfop) != 0) + ? -errno : stol_signo[rc]); +} + +long +lx_rt_sigtimedwait(uintptr_t set, uintptr_t sinfo, uintptr_t toutp, + uintptr_t setsize) +{ + sigset_t s_set; + siginfo_t s_sinfo, *s_sinfop; + int rc; + + lx_sigset_t *setp = (lx_sigset_t *)set; + lx_siginfo_t *sinfop = (lx_siginfo_t *)sinfo; + + if ((size_t)setsize != sizeof (lx_sigset_t)) + return (-EINVAL); + + if (ltos_sigset(setp, &s_set) != 0) + return (-errno); + + s_sinfop = (sinfop == NULL) ? NULL : &s_sinfo; + + /* + * "If timeout is the NULL pointer, the behavior is unspecified." + * Match what LTP expects. + */ + if ((rc = sigtimedwait(&s_set, s_sinfop, + (struct timespec *)toutp)) == -1) + return (toutp == (uintptr_t)NULL ? -EINTR : -errno); + + if (s_sinfop == NULL) + return (stol_signo[rc]); + + return ((stol_siginfo(s_sinfop, sinfop) != 0) + ? -errno : stol_signo[rc]); +} + +static void +lx_sigreturn_find_native_context(const char *caller, ucontext_t **sigucp, + ucontext_t **retucp, uintptr_t sp) +{ + lx_tsd_t *lxtsd = lx_get_tsd(); + lx_sigdeliver_frame_t *lxsdfp = (lx_sigdeliver_frame_t *)sp; + lx_sigdeliver_frame_t lxsdf; + boolean_t copy_ok; + + lx_debug("%s: reading lx_sigdeliver_frame_t @ %p\n", caller, lxsdfp); + if (uucopy(lxsdfp, &lxsdf, sizeof (lxsdf)) != 0) { + lx_debug("%s: failed to read lx_sigdeliver_frame_t @ %p\n", + lxsdfp); + + copy_ok = B_FALSE; + } else { + lx_debug("%s: lxsdf: magic %p retucp %p sigucp %p\n", caller, + lxsdf.lxsdf_magic, lxsdf.lxsdf_retucp, lxsdf.lxsdf_sigucp); + + copy_ok = B_TRUE; + } + + /* + * lx_sigdeliver() pushes a lx_sigdeliver_frame_t onto the stack + * before it creates the struct lx_oldsigstack. + */ + if (copy_ok && lxsdf.lxsdf_magic == LX_SIGRT_MAGIC) { + LX_SIGNAL_DELIVERY_FRAME_FOUND(lxsdfp); + + /* + * The guard value is intact; use the context pointers stored + * in the signal delivery frame: + */ + *sigucp = lxsdf.lxsdf_sigucp; + *retucp = lxsdf.lxsdf_retucp; + + /* + * Ensure that the backup signal delivery chain is in sync with + * the frame we are returning via: + */ + lxtsd->lxtsd_sigbackup = lxsdf.lxsdf_sigbackup; + } else { + /* + * The guard value was not intact. Either the program smashed + * the stack unintentionally, or worse: intentionally moved + * some parts of the signal delivery frame we constructed to + * another location before calling rt_sigreturn(2). + */ + LX_SIGNAL_DELIVERY_FRAME_CORRUPT(lxsdfp); + + if (lxtsd->lxtsd_sigbackup == NULL) { + /* + * There was no backup context to use, so we must + * kill the process. + */ + if (copy_ok) { + lx_err_fatal("%s: sp 0x%p, expected 0x%x, " + "found 0x%lx!", caller, (void *)sp, + LX_SIGRT_MAGIC, + (unsigned long)lxsdf.lxsdf_magic); + } else { + lx_err_fatal("%s: sp 0x%p, could not read " + "magic", caller, (void *)sp); + } + } + + /* + * Attempt to recover by using the backup signal delivery + * chain: + */ + lx_debug("%s: SIGRT_MAGIC not found @ sp %p; using backup " + "@ %p\n", caller, (void *)sp, lxtsd->lxtsd_sigbackup); + *sigucp = lxtsd->lxtsd_sigbackup->lxsb_sigucp; + *retucp = lxtsd->lxtsd_sigbackup->lxsb_retucp; + } +} + +#if defined(_ILP32) +/* + * Intercept the Linux sigreturn() syscall to turn it into the return through + * the libc call stack that Illumos expects. + * + * When control returns to libc's call_user_handler() routine, a setcontext(2) + * will be done that returns thread execution to the point originally + * interrupted by receipt of the signal. + * + * This is only used by 32-bit code. + */ +long +lx_sigreturn(void) +{ + struct lx_oldsigstack *lx_ossp; + lx_sigset_t lx_sigset; + ucontext_t *ucp; + ucontext_t *sigucp; + ucontext_t *retucp; + uintptr_t sp; + + ucp = lx_syscall_regs(); + + /* + * NOTE: The sp saved in the context is eight bytes off of where we + * need it to be (either due to trampoline or the copying of + * sp = uesp, not clear which). + */ + sp = LX_REG(ucp, REG_SP) - 8; + + /* + * At this point, the stack pointer should point to the struct + * lx_oldsigstack that lx_build_old_signal_frame() constructed and + * placed on the stack. We need to reference it a bit later, so + * save a pointer to it before incrementing our copy of the sp. + */ + lx_ossp = (struct lx_oldsigstack *)sp; + sp += SA(sizeof (struct lx_oldsigstack)); + + lx_sigreturn_find_native_context(__func__, &sigucp, &retucp, sp); + + /* + * We need to copy machine registers the Linux signal handler may have + * modified back to the Illumos ucontext_t. + * + * General registers copy across as-is, except Linux expects that + * changes made to uc_mcontext.gregs[ESP] will be reflected when the + * interrupted thread resumes execution after the signal handler. To + * emulate this behavior, we must modify uc_mcontext.gregs[UESP] to + * match uc_mcontext.gregs[ESP] as Illumos will restore the UESP + * value to ESP. + */ + lx_ossp->sigc.sc_esp_at_signal = lx_ossp->sigc.sc_esp; + bcopy(&lx_ossp->sigc, &sigucp->uc_mcontext, sizeof (gregset_t)); + + LX_SIGRETURN(NULL, sigucp, sp); + + /* copy back FP regs if present */ + if (lx_ossp->sigc.sc_fpstate != NULL) + ltos_fpstate(&lx_ossp->fpstate, &sigucp->uc_mcontext.fpregs); + + /* convert Linux signal mask back to its Illumos equivalent */ + bzero(&lx_sigset, sizeof (lx_sigset_t)); + lx_sigset.__bits[0] = lx_ossp->sigc.sc_mask; + lx_sigset.__bits[1] = lx_ossp->sig_extra; + (void) ltos_sigset(&lx_sigset, &sigucp->uc_sigmask); + + /* + * For signal mask handling to be done properly, this call needs to + * return to the libc routine that originally called the signal handler + * rather than directly set the context back to the place the signal + * interrupted execution as the original Linux code would do. + */ + lx_debug("lx_sigreturn: calling setcontext; retucp %p flags %lx " + "link %p\n", retucp, retucp->uc_flags, retucp->uc_link); + (void) setcontext(retucp); + assert(0); + + /*NOTREACHED*/ + return (0); +} +#endif + +/* + * This signal return syscall is used by both 32-bit and 64-bit code. + */ +long +lx_rt_sigreturn(void) +{ + struct lx_sigstack *lx_ssp; + lx_ucontext_t *lx_ucp; + ucontext_t *ucp; + ucontext_t *sigucp; + ucontext_t *retucp; + uintptr_t sp; + + /* + * Since we don't take the normal return path from this syscall, we + * inform the kernel that we're returning, for the sake of ptrace. + */ + (void) syscall(SYS_brand, B_PTRACE_SIG_RETURN); + + /* Get the registers at the emulated Linux rt_sigreturn syscall */ + ucp = lx_syscall_regs(); + +#if defined(_ILP32) + lx_debug("lx_rt_sigreturn: ESP %p UESP %p\n", LX_REG(ucp, ESP), + LX_REG(ucp, UESP)); + /* + * For 32-bit + * + * NOTE: Because of the silly compatibility measures done in the + * signal trampoline code to make sure the stack holds the + * _exact same_ instruction sequence Linux does, we have to + * manually "pop" some extra instructions off the stack here + * before passing the stack address to the syscall because the + * trampoline code isn't allowed to do it due to the gdb + * compatability issues. + * + * No, I'm not kidding. + * + * The sp saved in the context is eight bytes off of where we + * need it to be (either due to trampoline or the copying of + * sp = uesp, not clear which but looks like the uesp case), so + * the need to pop the extra four byte instruction means we need + * to subtract a net four bytes from the sp before "popping" the + * struct lx_sigstack off the stack. + * + * This will yield the value the stack pointer had before + * lx_sigdeliver() created the stack frame for the Linux signal + * handler. + */ + sp = (uintptr_t)LX_REG(ucp, REG_SP) - 4; +#else + /* + * We need to make an adjustment for 64-bit code as well. Since 64-bit + * does not use the trampoline, it's probably for the same reason as + * alluded to above. + */ + sp = (uintptr_t)LX_REG(ucp, REG_SP) - 8; +#endif + + /* + * At this point, the stack pointer should point to the struct + * lx_sigstack that lx_build_signal_frame() constructed and + * placed on the stack. We need to reference it a bit later, so + * save a pointer to it before incrementing our copy of the sp. + */ + lx_ssp = (struct lx_sigstack *)sp; + sp += SA(sizeof (struct lx_sigstack)); + +#if defined(_LP64) + /* + * The 64-bit lx_sigdeliver() inserts 8 bytes of padding between + * the lx_sigstack_t and the delivery frame to maintain ABI stack + * alignment. + */ + sp += 8; +#endif + + lx_sigreturn_find_native_context(__func__, &sigucp, &retucp, sp); + + /* + * We need to copy machine registers the Linux signal handler may have + * modified back to the Illumos version. + */ +#if defined(_LP64) + lx_ucp = &lx_ssp->uc; + + /* + * General register layout is completely different. + */ + LX_REG(sigucp, REG_R15) = lx_ucp->uc_sigcontext.sc_r15; + LX_REG(sigucp, REG_R14) = lx_ucp->uc_sigcontext.sc_r14; + LX_REG(sigucp, REG_R13) = lx_ucp->uc_sigcontext.sc_r13; + LX_REG(sigucp, REG_R12) = lx_ucp->uc_sigcontext.sc_r12; + LX_REG(sigucp, REG_R11) = lx_ucp->uc_sigcontext.sc_r11; + LX_REG(sigucp, REG_R10) = lx_ucp->uc_sigcontext.sc_r10; + LX_REG(sigucp, REG_R9) = lx_ucp->uc_sigcontext.sc_r9; + LX_REG(sigucp, REG_R8) = lx_ucp->uc_sigcontext.sc_r8; + LX_REG(sigucp, REG_RDI) = lx_ucp->uc_sigcontext.sc_rdi; + LX_REG(sigucp, REG_RSI) = lx_ucp->uc_sigcontext.sc_rsi; + LX_REG(sigucp, REG_RBP) = lx_ucp->uc_sigcontext.sc_rbp; + LX_REG(sigucp, REG_RBX) = lx_ucp->uc_sigcontext.sc_rbx; + LX_REG(sigucp, REG_RDX) = lx_ucp->uc_sigcontext.sc_rdx; + LX_REG(sigucp, REG_RCX) = lx_ucp->uc_sigcontext.sc_rcx; + LX_REG(sigucp, REG_RAX) = lx_ucp->uc_sigcontext.sc_rax; + LX_REG(sigucp, REG_TRAPNO) = lx_ucp->uc_sigcontext.sc_trapno; + LX_REG(sigucp, REG_ERR) = lx_ucp->uc_sigcontext.sc_err; + LX_REG(sigucp, REG_RIP) = lx_ucp->uc_sigcontext.sc_rip; + LX_REG(sigucp, REG_CS) = lx_ucp->uc_sigcontext.sc_cs; + LX_REG(sigucp, REG_RFL) = lx_ucp->uc_sigcontext.sc_eflags; + LX_REG(sigucp, REG_RSP) = lx_ucp->uc_sigcontext.sc_rsp; + LX_REG(sigucp, REG_SS) = lx_ucp->uc_sigcontext.sc_pad0; + LX_REG(sigucp, REG_FS) = lx_ucp->uc_sigcontext.sc_fs; + LX_REG(sigucp, REG_GS) = lx_ucp->uc_sigcontext.sc_gs; + +#else /* is _ILP32 */ + lx_ucp = &lx_ssp->uc; + + /* + * Illumos and Linux both follow the SysV i386 ABI layout for the + * mcontext. + * + * General registers copy across as-is, except Linux expects that + * changes made to uc_mcontext.gregs[ESP] will be reflected when the + * interrupted thread resumes execution after the signal handler. To + * emulate this behavior, we must modify uc_mcontext.gregs[UESP] to + * match uc_mcontext.gregs[ESP] as Illumos will restore the UESP value + * to ESP. + */ + lx_ucp->uc_sigcontext.sc_esp_at_signal = lx_ucp->uc_sigcontext.sc_esp; + + bcopy(&lx_ucp->uc_sigcontext, &sigucp->uc_mcontext.gregs, + sizeof (gregset_t)); +#endif + + LX_SIGRETURN(lx_ucp, sigucp, sp); + + if (lx_ucp->uc_sigcontext.sc_fpstate != NULL) { + ltos_fpstate(lx_ucp->uc_sigcontext.sc_fpstate, + &sigucp->uc_mcontext.fpregs); + } + + /* + * Convert the Linux signal mask and stack back to their + * Illumos equivalents. + */ + (void) ltos_sigset(&lx_ucp->uc_sigmask, &sigucp->uc_sigmask); + ltos_stack(&lx_ucp->uc_stack, &sigucp->uc_stack); + + /* + * For signal mask handling to be done properly, this call needs to + * return to the libc routine that originally called the signal handler + * rather than directly set the context back to the place the signal + * interrupted execution as the original Linux code would do. + */ + lx_debug("lx_rt_sigreturn: calling setcontext; retucp %p\n", retucp); + (void) setcontext(retucp); + assert(0); + + /*NOTREACHED*/ + return (0); +} + + +#if defined(_ILP32) +/* + * Build signal frame for processing for "old" (legacy) Linux signals + * This stack-builder function is only used by 32-bit code. + */ +/* ARGSUSED4 */ +static void +lx_build_old_signal_frame(int lx_sig, siginfo_t *sip, void *p, void *sp, + uintptr_t *hargs) +{ + extern void lx_sigreturn_tramp(); + + lx_sigset_t lx_sigset; + ucontext_t *ucp = (ucontext_t *)p; + struct lx_sigaction *lxsap; + struct lx_oldsigstack *lx_ossp = sp; + + lx_debug("building old signal frame for lx sig %d at 0x%p", lx_sig, sp); + + lx_ossp->sig = lx_sig; + lxsap = &lx_sighandlers.lx_sa[lx_sig]; + lx_debug("lxsap @ 0x%p", lxsap); + + if (lxsap && (lxsap->lxsa_flags & LX_SA_RESTORER) && + lxsap->lxsa_restorer) { + lx_ossp->retaddr = lxsap->lxsa_restorer; + lx_debug("lxsa_restorer exists @ 0x%p", lx_ossp->retaddr); + } else { + lx_ossp->retaddr = lx_sigreturn_tramp; + lx_debug("lx_ossp->retaddr set to 0x%p", lx_sigreturn_tramp); + } + + lx_debug("osf retaddr = 0x%p", lx_ossp->retaddr); + + /* convert Illumos signal mask and stack to their Linux equivalents */ + (void) stol_sigset(&ucp->uc_sigmask, &lx_sigset); + lx_ossp->sigc.sc_mask = lx_sigset.__bits[0]; + lx_ossp->sig_extra = lx_sigset.__bits[1]; + + /* + * General registers copy across as-is, except Linux expects that + * uc_mcontext.gregs[ESP] == uc_mcontext.gregs[UESP] on receipt of a + * signal. + */ + bcopy(&ucp->uc_mcontext, &lx_ossp->sigc, sizeof (gregset_t)); + lx_ossp->sigc.sc_esp = lx_ossp->sigc.sc_esp_at_signal; + + /* + * cr2 contains the faulting address, and Linux only sets cr2 for a + * a segmentation fault. + */ + lx_ossp->sigc.sc_cr2 = (((lx_sig == LX_SIGSEGV) && (sip)) ? + (uintptr_t)sip->si_addr : 0); + + /* convert FP regs if present */ + if (ucp->uc_flags & UC_FPU) { + stol_fpstate(&ucp->uc_mcontext.fpregs, &lx_ossp->fpstate); + lx_ossp->sigc.sc_fpstate = &lx_ossp->fpstate; + } else { + lx_ossp->sigc.sc_fpstate = NULL; + } + + /* + * Believe it or not, gdb wants to SEE the trampoline code on the + * bottom of the stack to determine whether the stack frame belongs to + * a signal handler, even though this code is no longer actually + * called. + * + * You can't make this stuff up. + */ + bcopy((void *)lx_sigreturn_tramp, lx_ossp->trampoline, + sizeof (lx_ossp->trampoline)); +} +#endif + +/* + * Build stack frame (32-bit) or stack local data (64-bit) for processing for + * modern Linux signals. This is the only stack-builder function for 64-bit + * code (32-bit code also calls this when using "modern" signals). + */ +/* ARGSUSED4 */ +static void +lx_build_signal_frame(int lx_sig, siginfo_t *sip, void *p, void *sp, + uintptr_t *hargs) +{ + extern void lx_rt_sigreturn_tramp(); + + lx_ucontext_t *lx_ucp; + ucontext_t *ucp = (ucontext_t *)p; + struct lx_sigstack *lx_ssp = sp; + struct lx_sigaction *lxsap; + + lx_debug("building signal frame for lx sig %d at 0x%p", lx_sig, sp); + + lx_ucp = &lx_ssp->uc; +#if defined(_ILP32) + /* + * Arguments are passed to the 32-bit signal handler on the stack. + */ + lx_ssp->ucp = lx_ucp; + lx_ssp->sip = sip != NULL ? &lx_ssp->si : NULL; + lx_ssp->sig = lx_sig; +#else + /* + * Arguments to the 64-bit signal handler are passed in registers: + * hdlr(int sig, siginfo_t *sip, void *ucp); + */ + hargs[0] = lx_sig; + hargs[1] = sip != NULL ? (uintptr_t)&lx_ssp->si : (uintptr_t)NULL; + hargs[2] = (uintptr_t)lx_ucp; +#endif + + lxsap = &lx_sighandlers.lx_sa[lx_sig]; + lx_debug("lxsap @ 0x%p", lxsap); + + if (lxsap && (lxsap->lxsa_flags & LX_SA_RESTORER) && + lxsap->lxsa_restorer) { + /* + * lxsa_restorer is explicitly set by sigaction in 32-bit code + * but it can also be implicitly set for both 32 and 64 bit + * code via lx_sigaction_common when we bcopy the user-supplied + * lx_sigaction element into the proper slot in the sighandler + * array. + */ + lx_ssp->retaddr = lxsap->lxsa_restorer; + lx_debug("lxsa_restorer exists @ 0x%p", lx_ssp->retaddr); + } else { + lx_ssp->retaddr = lx_rt_sigreturn_tramp; + lx_debug("lx_ssp->retaddr set to 0x%p", lx_rt_sigreturn_tramp); + } + + /* Linux has these fields but always clears them to 0 */ + lx_ucp->uc_flags = 0; + lx_ucp->uc_link = NULL; + + /* convert Illumos signal mask and stack to their Linux equivalents */ + (void) stol_sigset(&ucp->uc_sigmask, &lx_ucp->uc_sigmask); + stol_stack(&ucp->uc_stack, &lx_ucp->uc_stack); + +#if defined(_LP64) + /* + * General register layout is completely different. + */ + lx_ucp->uc_sigcontext.sc_r8 = LX_REG(ucp, REG_R8); + lx_ucp->uc_sigcontext.sc_r9 = LX_REG(ucp, REG_R9); + lx_ucp->uc_sigcontext.sc_r10 = LX_REG(ucp, REG_R10); + lx_ucp->uc_sigcontext.sc_r11 = LX_REG(ucp, REG_R11); + lx_ucp->uc_sigcontext.sc_r12 = LX_REG(ucp, REG_R12); + lx_ucp->uc_sigcontext.sc_r13 = LX_REG(ucp, REG_R13); + lx_ucp->uc_sigcontext.sc_r14 = LX_REG(ucp, REG_R14); + lx_ucp->uc_sigcontext.sc_r15 = LX_REG(ucp, REG_R15); + lx_ucp->uc_sigcontext.sc_rdi = LX_REG(ucp, REG_RDI); + lx_ucp->uc_sigcontext.sc_rsi = LX_REG(ucp, REG_RSI); + lx_ucp->uc_sigcontext.sc_rbp = LX_REG(ucp, REG_RBP); + lx_ucp->uc_sigcontext.sc_rbx = LX_REG(ucp, REG_RBX); + lx_ucp->uc_sigcontext.sc_rdx = LX_REG(ucp, REG_RDX); + lx_ucp->uc_sigcontext.sc_rax = LX_REG(ucp, REG_RAX); + lx_ucp->uc_sigcontext.sc_rcx = LX_REG(ucp, REG_RCX); + lx_ucp->uc_sigcontext.sc_rsp = LX_REG(ucp, REG_RSP); + lx_ucp->uc_sigcontext.sc_rip = LX_REG(ucp, REG_RIP); + lx_ucp->uc_sigcontext.sc_eflags = LX_REG(ucp, REG_RFL); + lx_ucp->uc_sigcontext.sc_cs = LX_REG(ucp, REG_CS); + lx_ucp->uc_sigcontext.sc_gs = LX_REG(ucp, REG_GS); + lx_ucp->uc_sigcontext.sc_fs = LX_REG(ucp, REG_FS); + lx_ucp->uc_sigcontext.sc_pad0 = LX_REG(ucp, REG_SS); + lx_ucp->uc_sigcontext.sc_err = LX_REG(ucp, REG_ERR); + lx_ucp->uc_sigcontext.sc_trapno = LX_REG(ucp, REG_TRAPNO); + +#else /* is _ILP32 */ + /* + * General registers copy across as-is, except Linux expects that + * uc_mcontext.gregs[ESP] == uc_mcontext.gregs[UESP] on receipt of a + * signal. + */ + bcopy(&ucp->uc_mcontext, &lx_ucp->uc_sigcontext, sizeof (gregset_t)); + lx_ucp->uc_sigcontext.sc_esp = lx_ucp->uc_sigcontext.sc_esp_at_signal; +#endif + + /* + * cr2 contains the faulting address, which Linux only sets for a + * a segmentation fault. + */ + lx_ucp->uc_sigcontext.sc_cr2 = ((lx_sig == LX_SIGSEGV) && (sip)) ? + (uintptr_t)sip->si_addr : 0; + + /* + * This should only return an error if the signum is invalid but that + * also gets converted into a LX_SIGKILL by this function. + */ + if (sip != NULL) + (void) stol_siginfo(sip, &lx_ssp->si); + else + bzero(&lx_ssp->si, sizeof (lx_siginfo_t)); + + /* convert FP regs if present */ + if (ucp->uc_flags & UC_FPU) { + /* + * Copy FP regs to the appropriate place in the the lx_sigstack + * structure. + */ + stol_fpstate(&ucp->uc_mcontext.fpregs, &lx_ssp->fpstate); + lx_ucp->uc_sigcontext.sc_fpstate = &lx_ssp->fpstate; + } else { + lx_ucp->uc_sigcontext.sc_fpstate = NULL; + } + +#if defined(_ILP32) + /* + * Believe it or not, gdb wants to SEE the sigreturn code on the + * top of the stack to determine whether the stack frame belongs to + * a signal handler, even though this code is not actually called. + * + * You can't make this stuff up. + */ + bcopy((void *)lx_rt_sigreturn_tramp, lx_ssp->trampoline, + sizeof (lx_ssp->trampoline)); +#endif +} + +/* + * This is the interposition handler for Linux signals. + */ +static void +lx_call_user_handler(int sig, siginfo_t *sip, void *p) +{ + void (*user_handler)(); + void (*stk_builder)(); + volatile struct lx_sigaction *lxsap; + ucontext_t *ucp = (ucontext_t *)p; + size_t stksize; + int lx_sig; + + /* + * If Illumos signal has no Linux equivalent, effectively ignore it. + */ + if ((lx_sig = stol_signo[sig]) == -1) { + lx_unsupported("caught Illumos signal %d, no Linux equivalent", + sig); + return; + } + + lx_debug("interpose caught Illumos signal %d, translating to Linux " + "signal %d", sig, lx_sig); + + lxsap = &lx_sighandlers.lx_sa[lx_sig]; + lx_debug("lxsap @ 0x%p", lxsap); + + if ((sig == SIGPWR) && (lxsap->lxsa_handler == SIG_DFL)) { + /* + * Linux SIG_DFL for SIGPWR is to terminate. The lx wait + * emulation will translate SIGPWR to LX_SIGPWR. + */ + (void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGPWR); + /* This should never return */ + assert(0); + } + + while (lxsap->lxsa_handler == SIG_DFL || + lxsap->lxsa_handler == SIG_IGN) { + /* + * This normally shouldn't be possible, but there is a window + * in which a vfork()'d process can have its signal disposition + * corrupted by its child. While this window is narrowed by + * blocking all signals in the brand, that leaves a (smaller) + * window whereby a signal in flight is delivered before the + * brand has blocked them. To detect this case, we will spin + * if our signal disposition is impossible and all signals are + * blocked due to vfork() activity: we know that the vfork()'d + * child will eventually restore the signal disposition before + * it unblocks signals, allowing us to proceed. + */ + if (lx_all_signals_blocked()) + continue; + + if (lxsap->lxsa_handler != SIG_DFL && + lxsap->lxsa_handler != SIG_IGN) + break; + + lx_err_fatal("lxsa_handler set to %s? How?!?!?", + (lxsap->lxsa_handler == SIG_DFL) ? "SIG_DFL" : "SIG_IGN"); + } + +#if defined(_LP64) + stksize = sizeof (struct lx_sigstack); + stk_builder = lx_build_signal_frame; +#else + if (lxsap->lxsa_flags & LX_SA_SIGINFO) { + stksize = sizeof (struct lx_sigstack); + stk_builder = lx_build_signal_frame; + } else { + stksize = sizeof (struct lx_oldsigstack); + stk_builder = lx_build_old_signal_frame; + } +#endif + + user_handler = lxsap->lxsa_handler; + + lx_debug("delivering %d (lx %d) to handler at 0x%p", sig, lx_sig, + lxsap->lxsa_handler); + + if (lxsap->lxsa_flags & LX_SA_RESETHAND) + lxsap->lxsa_handler = SIG_DFL; + + lx_sigdeliver(lx_sig, sip, ucp, stksize, stk_builder, user_handler, + (struct lx_sigaction *)lxsap); + + /* + * We need to handle restarting system calls if requested by the + * program for this signal type: + */ + if (lxsap->lxsa_flags & LX_SA_RESTART) { + uintptr_t flags = (uintptr_t)ucp->uc_brand_data[0]; + long ret = (long)LX_REG(ucp, REG_R0); + boolean_t interrupted = (ret == -lx_errno(EINTR, -1)); + + /* + * If the system call returned EINTR, and the system + * call handler set "br_syscall_restart" when returning, + * we modify the context to try the system call again + * when we return from this signal handler. + */ + if ((flags & LX_UC_RESTART_SYSCALL) && interrupted) { + int syscall_num = (int)(uintptr_t)ucp->uc_brand_data[2]; + + lx_debug("restarting interrupted system call %d", + syscall_num); + + /* + * Both the "int 0x80" and the "syscall" instruction + * are two bytes long. Wind the program counter back + * to the start of this instruction. + * + * The system call we interrupted is preserved in the + * brand-specific data in the ucontext_t when the + * LX_UC_RESTART_SYSCALL flag is set. This is + * analogous to the "orig_[er]ax" field in the Linux + * "user_regs_struct". + */ + LX_REG(ucp, REG_PC) -= 2; + LX_REG(ucp, REG_R0) = syscall_num; + } + } +} + +/* + * The "lx_sigdeliver()" function is responsible for constructing the emulated + * signal delivery frame on the brand stack for this LWP. A context is saved + * on the stack which will be used by the "sigreturn(2)" family of emulated + * system calls to get us back here after the Linux signal handler returns. + * This function is modelled on the in-kernel "sendsig()" signal delivery + * mechanism. + */ +void +lx_sigdeliver(int lx_sig, siginfo_t *sip, ucontext_t *ucp, size_t stacksz, + void (*stack_builder)(), void (*user_handler)(), + struct lx_sigaction *lxsap) +{ + lx_sigbackup_t sigbackup; + ucontext_t uc; + lx_tsd_t *lxtsd = lx_get_tsd(); + int totsz = 0; + uintptr_t flags; + uintptr_t hargs[3]; + uintptr_t orig_sp = 0; + + /* + * These variables must be "volatile", as they are modified after the + * getcontext() stores the register state: + */ + volatile boolean_t signal_delivered = B_FALSE; + volatile boolean_t sp_modified = B_FALSE; + volatile uintptr_t lxfp = 0; + volatile uintptr_t old_tsd_sp = 0; + volatile int newstack = 0; + + /* + * This function involves modifying the Linux process stack for this + * thread. To do so without corruption requires us to exclude other + * signal handlers (or emulated system calls called from within those + * handlers) from running while we reserve space on that stack. We + * defer the execution of further instances of lx_call_user_handler() + * until we have completed this operation. + */ + _sigoff(); + + /* + * Clear register arguments vector. + */ + bzero(hargs, sizeof (hargs)); + + /* Save our SP so we can restore it after coming back in. */ + orig_sp = LX_REG(ucp, REG_SP); + + /* + * We save a context here so that we can be returned later to complete + * handling the signal. + */ + lx_debug("lx_sigdeliver: STORING RETURN CONTEXT @ %p\n", &uc); + assert(getcontext(&uc) == 0); + lx_debug("lx_sigdeliver: RETURN CONTEXT %p LINK %p FLAGS %lx\n", + &uc, uc.uc_link, uc.uc_flags); + if (signal_delivered) { + /* + * If the "signal_delivered" flag is set, we are returned here + * via setcontext() as called by the emulated Linux signal + * return system call. + */ + lx_debug("lx_sigdeliver: WE ARE BACK, VIA UC @ %p!\n", &uc); + + if (sp_modified) { + /* + * Restore the original stack pointer, which we saved + * on our alt. stack, back into the context. + */ + LX_REG(ucp, REG_SP) = orig_sp; + } + + goto after_signal_handler; + } + signal_delivered = B_TRUE; + + /* + * Preserve the current tsd value of the Linux process stack pointer, + * even if it is zero. We will restore it when we are returned here + * via setcontext() after the Linux process has completed execution of + * its signal handler. + */ + old_tsd_sp = lxtsd->lxtsd_lx_sp; + + /* + * Figure out whether we will be handling this signal on an alternate + * stack specified by the user. + */ + newstack = (lxsap->lxsa_flags & LX_SA_ONSTACK) && + !(lxtsd->lxtsd_sigaltstack.ss_flags & (LX_SS_ONSTACK | + LX_SS_DISABLE)); + + /* + * Find the first unused region of the Linux process stack, where + * we will assemble our signal delivery frame. + */ + flags = (uintptr_t)ucp->uc_brand_data[0]; + if (newstack) { + /* + * We are moving to the user-provided alternate signal + * stack. + */ + lxfp = SA((uintptr_t)lxtsd->lxtsd_sigaltstack.ss_sp) + + SA(lxtsd->lxtsd_sigaltstack.ss_size) - STACK_ALIGN; + lx_debug("lx_sigdeliver: moving to ALTSTACK sp %p\n", lxfp); + LX_SIGNAL_ALTSTACK_ENABLE(lxfp); + } else if (flags & LX_UC_STACK_BRAND) { + /* + * We interrupted the Linux process to take this signal. The + * stack pointer is the one saved in this context. + */ + lxfp = LX_REG(ucp, REG_SP); + } else { + /* + * We interrupted a native (emulation) routine, so we must get + * the current stack pointer from either the tsd (if one is + * stored there) or via the context chain. + * + */ + lxfp = lx_find_brand_sp(); + if (lxtsd->lxtsd_lx_sp != 0) { + /* + * We must also make room for the possibility of nested + * signal delivery -- we may be pre-empting the + * in-progress handling of another signal. + * + * Note that if we were already on the alternate stack, + * any emulated Linux system calls would be betwixt + * that original signal frame and this new one on the + * one contiguous stack, so this logic holds either + * way: + */ + lxfp = MIN(lxtsd->lxtsd_lx_sp, lxfp); + } + + /* Replace the context SP with the one from the Linux context */ + LX_REG(ucp, REG_SP) = lxfp; + sp_modified = B_TRUE; + } + + /* + * Account for a reserved stack region (for amd64, this is 128 bytes), + * and align the stack: + */ + lxfp -= STACK_RESERVE; + lxfp &= ~(STACK_ALIGN - 1); + + /* + * Allocate space on the Linux process stack for our delivery frame, + * including: + * + * ----------------------------------------------------- old %sp + * - lx_sigdeliver_frame_t + * - (ucontext_t pointers and stack magic) + * ----------------------------------------------------- + * - (amd64-only 8-byte alignment gap) + * ----------------------------------------------------- + * - frame of size "stacksz" from the stack builder + * ----------------------------------------------------- new %sp + */ +#if defined(_LP64) + /* + * The AMD64 ABI requires us to align the stack such that when the + * called function pushes the base pointer, the stack is 16 byte + * aligned. The stack must, therefore, be 8- but _not_ 16-byte + * aligned. + */ +#if (STACK_ALIGN != 16) || (STACK_ENTRY_ALIGN != 8) +#error "lx_sigdeliver() did not find expected stack alignment" +#endif + totsz = SA(sizeof (lx_sigdeliver_frame_t)) + SA(stacksz) + 8; + assert((totsz & (STACK_ENTRY_ALIGN - 1)) == 0); + assert((totsz & (STACK_ALIGN - 1)) == 8); +#else + totsz = SA(sizeof (lx_sigdeliver_frame_t)) + SA(stacksz); + assert((totsz & (STACK_ALIGN - 1)) == 0); +#endif + + /* + * Copy our return frame into place: + */ + lxfp -= SA(sizeof (lx_sigdeliver_frame_t)); + lx_debug("lx_sigdeliver: lx_sigdeliver_frame_t @ %p\n", lxfp); + { + lx_sigdeliver_frame_t frm; + + frm.lxsdf_magic = LX_SIGRT_MAGIC; + frm.lxsdf_retucp = &uc; + frm.lxsdf_sigucp = ucp; + frm.lxsdf_sigbackup = &sigbackup; + + lx_debug("lx_sigdeliver: retucp %p sigucp %p\n", + frm.lxsdf_retucp, frm.lxsdf_sigucp); + + if (uucopy(&frm, (void *)lxfp, sizeof (frm)) != 0) { + /* + * We could not modify the stack of the emulated Linux + * program. Act like the kernel and terminate the + * program with a segmentation violation. + */ + (void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGSEGV); + } + + LX_SIGNAL_DELIVERY_FRAME_CREATE((void *)lxfp); + + /* + * Populate a backup copy of signal linkage to use in case + * the Linux program completely destroys (or relocates) the + * delivery frame. + * + * This is necessary for programs that have flown so far off + * the architectural rails that they believe it is + * acceptable to make assumptions about the precise size and + * layout of the signal handling frame assembled by the + * kernel. + */ + sigbackup.lxsb_retucp = frm.lxsdf_retucp; + sigbackup.lxsb_sigucp = frm.lxsdf_sigucp; + sigbackup.lxsb_sigdeliver_frame = lxfp; + sigbackup.lxsb_previous = lxtsd->lxtsd_sigbackup; + lxtsd->lxtsd_sigbackup = &sigbackup; + + lx_debug("lx_sigdeliver: installed sigbackup %p; prev %p\n", + &sigbackup, sigbackup.lxsb_previous); + } + + /* + * Build the Linux signal handling frame: + */ +#if defined(_LP64) + lxfp -= SA(stacksz) + 8; +#else + lxfp -= SA(stacksz); +#endif + lx_debug("lx_sigdeliver: Linux sig frame @ %p\n", lxfp); + stack_builder(lx_sig, sip, ucp, lxfp, hargs); + + /* + * Record our reservation so that any nested signal handlers + * can see it. + */ + lx_debug("lx_sigdeliver: Linux tsd sp %p -> %p\n", lxtsd->lxtsd_lx_sp, + lxfp); + lxtsd->lxtsd_lx_sp = lxfp; + + if (newstack) { + lxtsd->lxtsd_sigaltstack.ss_flags |= LX_SS_ONSTACK; + } + + LX_SIGDELIVER(lx_sig, lxsap, (void *)lxfp); + + /* + * Re-enable signal delivery. If a signal was queued while we were + * in the critical section, it will be delivered immediately. + */ + _sigon(); + + /* + * Pass control to the Linux signal handler: + */ + lx_debug("lx_sigdeliver: JUMPING TO LINUX (sig %d sp %p eip %p)\n", + lx_sig, lxfp, user_handler); + { + ucontext_t jump_uc; + + bcopy(lx_find_brand_uc(), &jump_uc, sizeof (jump_uc)); + + /* + * We want to load the general registers from this context, and + * switch to the BRAND stack. We do _not_ want to restore the + * uc_link value from this synthetic context, as that would + * break the signal handling context chain. + */ + jump_uc.uc_flags = UC_CPU; + jump_uc.uc_brand_data[0] = (void *)(LX_UC_STACK_BRAND | + LX_UC_IGNORE_LINK); + + LX_REG(&jump_uc, REG_FP) = 0; + LX_REG(&jump_uc, REG_SP) = lxfp; + LX_REG(&jump_uc, REG_PC) = (uintptr_t)user_handler; + +#if defined(_LP64) + /* + * Pass signal handler arguments by registers on AMD64. + */ + LX_REG(&jump_uc, REG_RDI) = hargs[0]; + LX_REG(&jump_uc, REG_RSI) = hargs[1]; + LX_REG(&jump_uc, REG_RDX) = hargs[2]; +#endif + + lx_jump_to_linux(&jump_uc); + } + + assert(0); + abort(); + +after_signal_handler: + /* + * Ensure all nested signal handlers have completed correctly + * and then remove our stack reservation. + */ + _sigoff(); + LX_SIGNAL_POST_HANDLER(lxfp, old_tsd_sp); + assert(lxtsd->lxtsd_lx_sp == lxfp); + lx_debug("lx_sigdeliver: after; Linux tsd sp %p -> %p\n", lxfp, + old_tsd_sp); + lxtsd->lxtsd_lx_sp = old_tsd_sp; + if (newstack) { + LX_SIGNAL_ALTSTACK_DISABLE(); + lx_debug("lx_sigdeliver: disabling ALTSTACK sp %p\n", lxfp); + lxtsd->lxtsd_sigaltstack.ss_flags &= ~LX_SS_ONSTACK; + } + /* + * Restore backup signal tracking chain pointer to previous value: + */ + if (lxtsd->lxtsd_sigbackup != NULL) { + lx_sigbackup_t *bprev = lxtsd->lxtsd_sigbackup->lxsb_previous; + + lx_debug("lx_sigdeliver: restoring sigbackup %p to %p\n", + lxtsd->lxtsd_sigbackup, bprev); + + lxtsd->lxtsd_sigbackup = bprev; + } + _sigon(); + + /* + * Here we return to libc so that it may clean up and restore the + * context originally interrupted by this signal. + */ +} + +/* + * Common routine to modify sigaction characteristics of a thread. + * + * We shouldn't need any special locking code here as we actually use our copy + * of libc's sigaction() to do all the real work, so its thread locking should + * take care of any issues for us. + */ +static int +lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp, + struct lx_sigaction *olxsp) +{ + struct lx_sigaction *lxsap; + struct sigaction sa; + + if (lx_sig <= 0 || lx_sig > LX_NSIG) + return (-EINVAL); + + lxsap = &lx_sighandlers.lx_sa[lx_sig]; + lx_debug("&lx_sighandlers.lx_sa[%d] = 0x%p", lx_sig, lxsap); + + if ((olxsp != NULL) && + ((uucopy(lxsap, olxsp, sizeof (struct lx_sigaction))) != 0)) + return (-errno); + + if (lxsp != NULL) { + int err, sig; + struct lx_sigaction lxsa; + sigset_t new_set, oset; + + if (uucopy(lxsp, &lxsa, sizeof (struct lx_sigaction)) != 0) + return (-errno); + + if ((sig = ltos_signo[lx_sig]) != -1) { + if (lx_no_abort_handler) { + /* + * If LX_NO_ABORT_HANDLER has been set, we will + * not allow the emulated program to do + * anything hamfisted with SIGSEGV or SIGABRT + * signals. + */ + if (sig == SIGSEGV || sig == SIGABRT) { + return (0); + } + } + + /* + * Block this signal while messing with its dispostion + */ + (void) sigemptyset(&new_set); + (void) sigaddset(&new_set, sig); + + if (sigprocmask(SIG_BLOCK, &new_set, &oset) < 0) { + err = errno; + lx_debug("unable to block signal %d: %d", + sig, err); + return (-err); + } + + /* + * We don't really need the old signal disposition at + * this point, but this weeds out signals that would + * cause sigaction() to return an error before we change + * anything other than the current signal mask. + */ + if (sigaction(sig, NULL, &sa) < 0) { + err = errno; + lx_debug("sigaction() to get old " + "disposition for signal %d failed: %d", + sig, err); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + return (-err); + } + + if ((lxsa.lxsa_handler != SIG_DFL) && + (lxsa.lxsa_handler != SIG_IGN)) { + sa.sa_handler = lx_call_user_handler; + + /* + * The interposition signal handler needs the + * information provided via the SA_SIGINFO flag. + */ + sa.sa_flags = SA_SIGINFO; + + /* + * When translating from Linux to illumos + * sigaction(2) flags, we explicitly do not + * pass SA_ONSTACK to the kernel. The + * alternate stack for Linux signal handling is + * handled entirely by the emulation code. + */ + if (lxsa.lxsa_flags & LX_SA_NOCLDSTOP) + sa.sa_flags |= SA_NOCLDSTOP; + if (lxsa.lxsa_flags & LX_SA_NOCLDWAIT) + sa.sa_flags |= SA_NOCLDWAIT; + if (lxsa.lxsa_flags & LX_SA_RESTART) + sa.sa_flags |= SA_RESTART; + if (lxsa.lxsa_flags & LX_SA_NODEFER) + sa.sa_flags |= SA_NODEFER; + + /* + * RESETHAND cannot be used be passed through + * for SIGPWR due to different default actions + * between Linux and Illumos. + */ + if ((sig != SIGPWR) && + (lxsa.lxsa_flags & LX_SA_RESETHAND)) + sa.sa_flags |= SA_RESETHAND; + + if (ltos_sigset(&lxsa.lxsa_mask, + &sa.sa_mask) != 0) { + err = errno; + (void) sigprocmask(SIG_SETMASK, &oset, + NULL); + return (-err); + } + + lx_debug("interposing handler @ 0x%p for " + "signal %d (lx %d), flags 0x%x", + lxsa.lxsa_handler, sig, lx_sig, + lxsa.lxsa_flags); + + if (sigaction(sig, &sa, NULL) < 0) { + err = errno; + lx_debug("sigaction() to set new " + "disposition for signal %d failed: " + "%d", sig, err); + (void) sigprocmask(SIG_SETMASK, &oset, + NULL); + return (-err); + } + } else if ((sig != SIGPWR) || + ((sig == SIGPWR) && + (lxsa.lxsa_handler == SIG_IGN))) { + /* + * There's no need to interpose for SIG_DFL or + * SIG_IGN so just call our copy of libc's + * sigaction(), but don't allow SIG_DFL for + * SIGPWR due to differing default actions + * between Linux and Illumos. + * + * Get the previous disposition first so things + * like sa_mask and sa_flags are preserved over + * a transition to SIG_DFL or SIG_IGN, which is + * what Linux expects. + */ + + sa.sa_handler = lxsa.lxsa_handler; + + if (sigaction(sig, &sa, NULL) < 0) { + err = errno; + lx_debug("sigaction(%d, %s) failed: %d", + sig, ((sa.sa_handler == SIG_DFL) ? + "SIG_DFL" : "SIG_IGN"), err); + (void) sigprocmask(SIG_SETMASK, &oset, + NULL); + return (-err); + } + } + } else { + lx_debug("Linux signal with no kill support " + "specified: %d", lx_sig); + } + + /* + * Save the new disposition for the signal in the global + * lx_sighandlers structure. + */ + bcopy(&lxsa, lxsap, sizeof (struct lx_sigaction)); + + /* + * Reset the signal mask to what we came in with if + * we were modifying a kill-supported signal. + */ + if (sig != -1) + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + } + + return (0); +} + +#if defined(_ILP32) +/* + * sigaction is only used in 32-bit code. + */ +long +lx_sigaction(uintptr_t lx_sig, uintptr_t actp, uintptr_t oactp) +{ + int val; + struct lx_sigaction sa, osa; + struct lx_sigaction *sap, *osap; + struct lx_osigaction *osp; + + sap = (actp ? &sa : NULL); + osap = (oactp ? &osa : NULL); + + /* + * If we have a source pointer, convert source lxsa_mask from + * lx_osigset_t to lx_sigset_t format. + */ + if (sap) { + osp = (struct lx_osigaction *)actp; + sap->lxsa_handler = osp->lxsa_handler; + + bzero(&sap->lxsa_mask, sizeof (lx_sigset_t)); + + for (val = 1; val <= OSIGSET_NBITS; val++) + if (osp->lxsa_mask & OSIGSET_BITSET(val)) + (void) lx_sigaddset(&sap->lxsa_mask, val); + + sap->lxsa_flags = osp->lxsa_flags; + sap->lxsa_restorer = osp->lxsa_restorer; + } + + if ((val = lx_sigaction_common(lx_sig, sap, osap))) + return (val); + + /* + * If we have a save pointer, convert the old lxsa_mask from + * lx_sigset_t to lx_osigset_t format. + */ + if (osap) { + osp = (struct lx_osigaction *)oactp; + + osp->lxsa_handler = osap->lxsa_handler; + + bzero(&osp->lxsa_mask, sizeof (osp->lxsa_mask)); + for (val = 1; val <= OSIGSET_NBITS; val++) + if (lx_sigismember(&osap->lxsa_mask, val)) + osp->lxsa_mask |= OSIGSET_BITSET(val); + + osp->lxsa_flags = osap->lxsa_flags; + osp->lxsa_restorer = osap->lxsa_restorer; + } + + return (0); +} +#endif + +long +lx_rt_sigaction(uintptr_t lx_sig, uintptr_t actp, uintptr_t oactp, + uintptr_t setsize) +{ + /* + * The "new" rt_sigaction call checks the setsize + * parameter. + */ + if ((size_t)setsize != sizeof (lx_sigset_t)) + return (-EINVAL); + + return (lx_sigaction_common(lx_sig, (struct lx_sigaction *)actp, + (struct lx_sigaction *)oactp)); +} + +#if defined(_ILP32) +/* + * Convert signal syscall to a call to the lx_sigaction() syscall + * Only used in 32-bit code. + */ +long +lx_signal(uintptr_t lx_sig, uintptr_t handler) +{ + struct sigaction act; + struct sigaction oact; + int rc; + + /* + * Use sigaction to mimic SYSV signal() behavior; glibc will + * actually call sigaction(2) itself, so we're really reaching + * back for signal(2) semantics here. + */ + bzero(&act, sizeof (act)); + act.sa_handler = (void (*)())handler; + act.sa_flags = SA_RESETHAND | SA_NODEFER; + + rc = lx_sigaction(lx_sig, (uintptr_t)&act, (uintptr_t)&oact); + return ((rc == 0) ? ((ssize_t)oact.sa_handler) : rc); +} +#endif + +void +lx_sighandlers_save(lx_sighandlers_t *saved) +{ + bcopy(&lx_sighandlers, saved, sizeof (lx_sighandlers_t)); +} + +void +lx_sighandlers_restore(lx_sighandlers_t *saved) +{ + bcopy(saved, &lx_sighandlers, sizeof (lx_sighandlers_t)); +} + +int +lx_siginit(void) +{ + extern void set_setcontext_enforcement(int); + extern void set_escaped_context_cleanup(int); + + struct sigaction sa; + sigset_t new_set, oset; + int lx_sig, sig; + + /* + * Block all signals possible while setting up the signal imposition + * mechanism. + */ + (void) sigfillset(&new_set); + + if (sigprocmask(SIG_BLOCK, &new_set, &oset) < 0) + lx_err_fatal("unable to block signals while setting up " + "imposition mechanism: %s", strerror(errno)); + + /* + * Ignore any signals that have no Linux analog so that those + * signals cannot be sent to Linux processes from the global zone + */ + for (sig = 1; sig < NSIG; sig++) + if (stol_signo[sig] < 0) + (void) sigignore(sig); + + /* + * Mark any signals that are ignored as ignored in our interposition + * handler array + */ + for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) { + if (((sig = ltos_signo[lx_sig]) != -1) && + (sigaction(sig, NULL, &sa) < 0)) + lx_err_fatal("unable to determine previous disposition " + "for signal %d: %s", sig, strerror(errno)); + + if (sa.sa_handler == SIG_IGN) { + lx_debug("marking signal %d (lx %d) as SIG_IGN", + sig, lx_sig); + lx_sighandlers.lx_sa[lx_sig].lxsa_handler = SIG_IGN; + } + } + + /* + * Have our interposition handler handle SIGPWR to start with, + * as it has a default action of terminating the process in Linux + * but its default is to be ignored in Illumos. + */ + (void) sigemptyset(&sa.sa_mask); + sa.sa_sigaction = lx_call_user_handler; + sa.sa_flags = SA_SIGINFO; + + if (sigaction(SIGPWR, &sa, NULL) < 0) + lx_err_fatal("sigaction(SIGPWR) failed: %s", strerror(errno)); + + /* + * Illumos' libc forces certain register values in the ucontext_t + * used to restore a post-signal user context to be those Illumos + * expects; however that is not what we want to happen if the signal + * was taken while branded code was executing, so we must disable + * that behavior. + */ + set_setcontext_enforcement(0); + + /* + * The illumos libc attempts to clean up dangling uc_link pointers in + * signal handling contexts when libc believes us to have escaped a + * signal handler incorrectly in the past. We want to disable this + * behaviour, so that the system call emulation context saved by the + * kernel brand module for lx_emulate() may be part of the context + * chain without itself being used for signal handling. + */ + set_escaped_context_cleanup(0); + + /* + * Reset the signal mask to what we came in with. + */ + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + + lx_debug("interposition handler setup for SIGPWR"); + return (0); +} + +/* + * The first argument is the pid (Linux tgid) to send the signal to, second + * argument is the signal to send (an lx signal), and third is the siginfo_t + * with extra information. We translate the code and signal only from the + * siginfo_t, and leave everything else the same as it gets passed through the + * signalling system. This is enough to get sigqueue working. See Linux man + * page rt_sigqueueinfo(2). + */ +long +lx_rt_sigqueueinfo(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + pid_t tgid = (pid_t)p1; + int lx_sig = (int)p2; + int sig; + lx_siginfo_t lx_siginfo; + siginfo_t siginfo; + int s_code; + pid_t s_pid; + + if (uucopy((void *)p3, &lx_siginfo, sizeof (lx_siginfo_t)) != 0) + return (-EFAULT); + s_code = ltos_sigcode(lx_siginfo.lsi_code); + if (s_code == LX_SI_CODE_NOT_EXIST) + return (-EINVAL); + if (lx_sig < 0 || lx_sig > LX_NSIG || (sig = ltos_signo[lx_sig]) < 0) { + return (-EINVAL); + } + /* + * This case (when trying to kill pid 0) just has a different errno + * returned in illumos than in Linux. + */ + if (tgid == 0) + return (-ESRCH); + if (lx_lpid_to_spid(tgid, &s_pid) != 0) + return (-ESRCH); + if (SI_CANQUEUE(s_code)) { + return ((syscall(SYS_sigqueue, s_pid, sig, + lx_siginfo.lsi_value, s_code, 0) == -1) ? + (-errno): 0); + } else { + /* + * This case is unlikely, as the main entry point is through + * sigqueue, which always has a queuable si_code. + */ + siginfo.si_signo = sig; + siginfo.si_code = s_code; + siginfo.si_pid = lx_siginfo.lsi_pid; + siginfo.si_value = lx_siginfo.lsi_value; + siginfo.si_uid = lx_siginfo.lsi_uid; + return ((syscall(SYS_brand, B_HELPER_SIGQUEUE, + tgid, sig, &siginfo)) ? (-errno) : 0); + } +} + +/* + * Adds an additional argument for which thread within a thread group to send + * the signal to (added as the second argument). + */ +long +lx_rt_tgsigqueueinfo(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + pid_t tgid = (pid_t)p1; + pid_t tid = (pid_t)p2; + int lx_sig = (int)p3; + int sig; + lx_siginfo_t lx_siginfo; + siginfo_t siginfo; + int si_code; + + if (uucopy((void *)p4, &lx_siginfo, sizeof (lx_siginfo_t)) != 0) + return (-EFAULT); + if (lx_sig < 0 || lx_sig > LX_NSIG || (sig = ltos_signo[lx_sig]) < 0) { + return (-EINVAL); + } + si_code = ltos_sigcode(lx_siginfo.lsi_code); + if (si_code == LX_SI_CODE_NOT_EXIST) + return (-EINVAL); + /* + * Check for invalid tgid and tids. That appears to be only negatives + * and 0 values. Everything else that doesn't exist is instead ESRCH. + */ + if (tgid <= 0 || tid <= 0) + return (-EINVAL); + siginfo.si_signo = sig; + siginfo.si_code = si_code; + siginfo.si_pid = lx_siginfo.lsi_pid; + siginfo.si_value = lx_siginfo.lsi_value; + siginfo.si_uid = lx_siginfo.lsi_uid; + + return ((syscall(SYS_brand, B_HELPER_TGSIGQUEUE, tgid, tid, sig, + &siginfo)) ? (-errno) : 0); +} + +long +lx_signalfd(int fd, uintptr_t mask, size_t msize) +{ + return (lx_signalfd4(fd, mask, msize, 0)); +} + +long +lx_signalfd4(int fd, uintptr_t mask, size_t msize, int flags) +{ + sigset_t s_set; + int r; + + if (msize != sizeof (int64_t)) + return (-EINVAL); + + if (ltos_sigset((lx_sigset_t *)mask, &s_set) != 0) + return (-errno); + + r = signalfd(fd, &s_set, flags); + + /* + * signalfd(3C) may fail with ENOENT if /dev/signalfd is not available. + * It is less jarring to Linux programs to tell them that internal + * allocation failed than to report an error number they are not + * expecting. + */ + if (r == -1 && errno == ENOENT) + return (-ENODEV); + + return (r == -1 ? -errno : r); +} + +void +lx_block_all_signals() +{ + (void) syscall(SYS_brand, B_BLOCK_ALL_SIGS); +} + +void +lx_unblock_all_signals() +{ + (void) syscall(SYS_brand, B_UNBLOCK_ALL_SIGS); +} + +int +lx_all_signals_blocked() +{ + return (syscall(SYS_brand, B_ALL_SIGS_BLOCKED)); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/stack.c b/usr/src/lib/brand/lx/lx_brand/common/stack.c new file mode 100644 index 0000000000..8f2c1cacc7 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/stack.c @@ -0,0 +1,337 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +/* + * Manage the native/emulation stack for LX-branded LWPs. + */ + +#include <assert.h> +#include <stdlib.h> +#include <strings.h> +#include <errno.h> + +#include <thread.h> +#include <sys/mman.h> +#include <sys/brand.h> +#include <sys/syscall.h> +#include <sys/debug.h> + +#include <sys/lx_brand.h> +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> +#include <sys/lx_thread.h> + + +typedef struct lx_stack_list_ent { + thread_t sle_tid; + void *sle_stack; + size_t sle_stack_size; + lx_tsd_t *sle_tsd; +} lx_stack_list_ent_t; + +static mutex_t lx_stack_list_lock = ERRORCHECKMUTEX; +lx_stack_list_ent_t *lx_stack_list = NULL; +unsigned int lx_stack_list_elems = 0; + +/* + * Usermode emulation alternate stack size, expressed as a page count: + */ +int lx_native_stack_page_count = LX_NATIVE_STACK_PAGE_COUNT; + +/* + * We use these private functions from libc to suspend signal delivery in + * critical sections: + */ +extern void _sigon(void); +extern void _sigoff(void); + +void +lx_stack_prefork(void) +{ + lx_tsd_t *lx_tsd = lx_get_tsd(); + + /* + * The "lx_stack_list_lock" mutex is used to protect access to the list + * of per-thread native stacks. Management of native stacks is + * generally performed while servicing an emulated fork(2), vfork(2) or + * clone(2) system call. + * + * Multiple threads may be attempting to create new threads or + * processes concurrently, but in the case of fork(2) only the + * currently executing thread is duplicated in the child process. We + * require that the stack list lock be taken before the native fork1() + * or forkx(), and released in both the parent and the child once the + * operation is complete. For vfork() the lock must only be released in + * the parent (once it resumes execution) since the child is borrowing + * the parent's thread. The _sigoff/_sigon dance will also only take + * place in the parent. + * + * Holding this mutex prevents the forked child from containing a + * copy-on-write copy of a locked mutex without the thread that would + * later unlock it. We also suspend signal delivery while entering + * this critical section to ensure async signal safety. + * + * Unfortunately some Linux applications (e.g. busybox) will call vfork + * and then call fork (without the expected intervening exec). We + * avoid the mutex deadlock by skipping the call since we know this + * thread has borrowed the parent's address space and the parent cannot + * execute until we exit/exec. + */ + _sigoff(); + if (lx_tsd->lxtsd_is_vforked == 0) + VERIFY0(mutex_lock(&lx_stack_list_lock)); +} + +void +lx_stack_postfork(void) +{ + lx_tsd_t *lx_tsd = lx_get_tsd(); + + if (lx_tsd->lxtsd_is_vforked == 0) + VERIFY0(mutex_unlock(&lx_stack_list_lock)); + _sigon(); +} + +/* + * Free the alternate stack for this thread. + */ +void +lx_free_stack(void) +{ + thread_t me = thr_self(); + int i; + + _sigoff(); + VERIFY0(mutex_lock(&lx_stack_list_lock)); + + /* + * Find this thread's stack in the list of stacks. + */ + for (i = 0; i < lx_stack_list_elems; i++) { + if (lx_stack_list[i].sle_tid != me) { + continue; + } + + (void) munmap(lx_stack_list[i].sle_stack, + lx_stack_list[i].sle_stack_size); + + /* + * Free the thread-specific data structure for this thread. + */ + if (lx_stack_list[i].sle_tsd != NULL) { + free(lx_stack_list[i].sle_tsd->lxtsd_clone_state); + free(lx_stack_list[i].sle_tsd); + } + + /* + * Free up this stack list entry: + */ + bzero(&lx_stack_list[i], sizeof (lx_stack_list[i])); + + VERIFY0(mutex_unlock(&lx_stack_list_lock)); + _sigon(); + return; + } + + /* + * Did not find the stack in the list. + */ + assert(0); +} + +/* + * After fork1(), we must unmap the stack of every thread other than the + * one copied into the child process. + */ +void +lx_free_other_stacks(void) +{ + int i, this_stack = -1; + thread_t me = thr_self(); + lx_tsd_t *lx_tsd = lx_get_tsd(); + + _sigoff(); + + /* + * We don't need to check or take the lx_stack_list_lock here because + * we are the only thread in this process, but if we got here via an + * evil vfork->fork path then we must drop the lock for the new child + * and reset our "is_vforked" counter. + */ + if (lx_tsd->lxtsd_is_vforked != 0) { + VERIFY0(mutex_unlock(&lx_stack_list_lock)); + lx_tsd->lxtsd_is_vforked = 0; + } + + for (i = 0; i < lx_stack_list_elems; i++) { + if (lx_stack_list[i].sle_tid == me) { + /* + * Do not unmap the stack for this LWP. + */ + this_stack = i; + continue; + } else if (lx_stack_list[i].sle_tid == 0) { + /* + * Skip any holes in the list. + */ + continue; + } + + /* + * Free the thread-specific data structure for this thread. + */ + if (lx_stack_list[i].sle_tsd != NULL) { + free(lx_stack_list[i].sle_tsd->lxtsd_clone_state); + free(lx_stack_list[i].sle_tsd); + } + + /* + * Unmap the stack of every other LWP. + */ + (void) munmap(lx_stack_list[i].sle_stack, + lx_stack_list[i].sle_stack_size); + } + /* + * Did not find the stack for this LWP in the list. + */ + assert(this_stack != -1); + + /* + * Ensure the stack data for this LWP is in the first slot and shrink + * the list. + */ + if (this_stack != 0) { + lx_stack_list[0] = lx_stack_list[this_stack]; + } + lx_stack_list_elems = 1; + lx_stack_list = realloc(lx_stack_list, lx_stack_list_elems * + sizeof (lx_stack_list[0])); + if (lx_stack_list == NULL) { + lx_err_fatal("failed to shrink stack list: %s", + strerror(errno)); + } + + _sigon(); +} + +/* + * Allocate an alternate stack for the execution of native emulation routines. + * This routine is based, in part, on find_stack() from libc. + */ +int +lx_alloc_stack(void **nstack, size_t *nstack_size) +{ + static int pagesize = 0; + static int stackprot = 0; + int stacksize = 0; + void *stack; + + /* + * Fetch configuration once: + */ + if (pagesize == 0) { + pagesize = _sysconf(_SC_PAGESIZE); + assert(pagesize > 0); + } + if (stackprot == 0) { + long lprot = _sysconf(_SC_STACK_PROT); + + stackprot = lprot > 0 ? lprot : (PROT_READ | PROT_WRITE); + } + + stacksize = lx_native_stack_page_count * pagesize; + + if ((stack = mmap(NULL, stacksize, stackprot, MAP_PRIVATE | + MAP_NORESERVE | MAP_ANON, -1, (off_t)0)) == MAP_FAILED) { + int en = errno; + lx_debug("lx_alloc_stack: failed to allocate stack: %d", errno); + errno = en; + return (-1); + } + +#if DEBUG + /* + * Write a recognisable pattern into the allocated stack pages. + */ + for (pos = 0; pos < ((stacksize - 1) / 4); pos++) { + ((uint32_t *)stack)[pos] = 0x0facade0; + } +#endif + + *nstack = stack; + *nstack_size = stacksize; + + return (0); +} + +/* + * Configure the in-kernel brand-specific LWP data with the native stack + * pointer for this thread. If a stack is not passed, allocate one first. + */ +void +lx_install_stack(void *stack, size_t stacksize, lx_tsd_t *tsd) +{ + thread_t me = thr_self(); + int i; + uintptr_t stack_top; + + if (stack == NULL) { + /* + * If we were not passed a stack, then allocate one: + */ + if (lx_alloc_stack(&stack, &stacksize) == -1) { + lx_err_fatal("failed to allocate stack for thread " + "%d: %s", me, strerror(errno)); + } + } + + /* + * Install the stack in the global list of thread stacks. + */ + _sigoff(); + VERIFY0(mutex_lock(&lx_stack_list_lock)); + + for (i = 0; i < lx_stack_list_elems; i++) { + assert(lx_stack_list[i].sle_tid != me); + if (lx_stack_list[i].sle_tid == 0) + break; + } + if (i >= lx_stack_list_elems) { + lx_stack_list_elems++; + lx_stack_list = realloc(lx_stack_list, lx_stack_list_elems * + sizeof (lx_stack_list[0])); + if (lx_stack_list == NULL) { + lx_err_fatal("failed to extend stack list: %s", + strerror(errno)); + } + } + lx_stack_list[i].sle_tid = me; + lx_stack_list[i].sle_stack = stack; + lx_stack_list[i].sle_stack_size = stacksize; + lx_stack_list[i].sle_tsd = tsd; + + VERIFY0(mutex_unlock(&lx_stack_list_lock)); + _sigon(); + + /* + * Inform the kernel of the location of the brand emulation + * stack for this LWP: + */ + stack_top = (uintptr_t)stack + stacksize; + lx_debug("stack %p stack_top %p\n", stack, stack_top); + if (syscall(SYS_brand, B_SET_NATIVE_STACK, stack_top) != 0) { + lx_err_fatal("unable to set native stack: %s", strerror(errno)); + } +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/statfs.c b/usr/src/lib/brand/lx/lx_brand/common/statfs.c new file mode 100644 index 0000000000..629e549831 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/statfs.c @@ -0,0 +1,348 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. All rights reserved. + */ + +#include <assert.h> +#include <errno.h> +#include <libintl.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/statvfs.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_statfs.h> +#include <sys/lx_syscall.h> + +/* + * these defines must exist before we include regexp.h, see regexp(5) + */ +#define RE_SIZE 1024 +#define INIT char *sp = instring; +#define GETC() (*sp++) +#define PEEKC() (*sp) +#define UNGETC(c) (--sp) +#define RETURN(c) return (NULL); +#define ERROR(c) return ((char *)c); + +/* + * for regular expressions we're using regexp(5). + * + * we'd really prefer to use some other nicer regular expressions + * interfaces (like regcmp(3c), regcomp(3c), or re_comp(3c)) but we + * can't because all these other interfaces rely on the ability + * to allocate memory via libc malloc()/calloc() calls, which + * we can't really do here. + * + * we could optionally use regexpr(3gen) but we don't since the + * interfaces there are incredibly similar to the regexp(5) + * interfaces we're already using and we'd have the added + * requirement of linking against libgen. + * + * another option that was considered is fnmatch(3c) but the + * limited pattern expansion capability of this interface would + * force us to include more patterns to check against. + */ +#include <regexp.h> + +static struct lx_ftype_path { + char *lfp_path; + char lfp_re[RE_SIZE]; + int lfp_magic; + char *lfp_magic_str; +} ftype_path_list[] = { + { "^/dev/pts$", "", + LX_DEVPTS_SUPER_MAGIC, "LX_DEVPTS_SUPER_MAGIC" }, + { "^/dev/pts/$", "", + LX_DEVPTS_SUPER_MAGIC, "LX_DEVPTS_SUPER_MAGIC" }, + { "^/dev/pts/[0-9][0-9]*$", "", + LX_DEVPTS_SUPER_MAGIC, "LX_DEVPTS_SUPER_MAGIC" }, + { NULL, "", + 0, NULL } +}; + +/* + * For lack of linux equivalents, we present lofs and zfs as being ext2. Yes, + * this is a total lie, but Linux can't handle the truth of ZFS -- and ext2's + * small surface area seems to make it the most tenable lie to tell. + */ +static struct lx_ftype_name { + const char *lfn_name; + int lfn_magic; + char *lfn_magic_str; +} ftype_name_list[] = { + { "hsfs", LX_ISOFS_SUPER_MAGIC, "LX_ISOFS_SUPER_MAGIC" }, + { "nfs", LX_NFS_SUPER_MAGIC, "LX_NFS_SUPER_MAGIC" }, + { "pcfs", LX_MSDOS_SUPER_MAGIC, "LX_MSDOS_SUPER_MAGIC" }, + { "lx_proc", LX_PROC_SUPER_MAGIC, "LX_PROC_SUPER_MAGIC" }, + { "tmpfs", LX_TMPFS_SUPER_MAGIC, "LX_TMPFS_SUPER_MAGIC" }, + { "ufs", LX_UFS_MAGIC, "LX_UFS_MAGIC" }, + { "lofs", LX_EXT2_SUPER_MAGIC, "LX_EXT2_SUPER_MAGIC" }, + { "zfs", LX_EXT2_SUPER_MAGIC, "LX_EXT2_SUPER_MAGIC" }, + { "lxautofs", LX_AUTOFS_SUPER_MAGIC, "LX_AUTOFS_SUPER_MAGIC" }, + { "lx_cgroup", LX_CGROUP_SUPER_MAGIC, "LX_CGROUP_SUPER_MAGIC" }, + { "lx_sysfs", LX_SYSFS_SUPER_MAGIC, "LX_SYSFS_SUPER_MAGIC" }, + { NULL, 0, NULL } +}; + +int +lx_statfs_init() +{ + int i; + char *rv; + + for (i = 0; ftype_path_list[i].lfp_path != NULL; i++) { + rv = compile( + ftype_path_list[i].lfp_path, + ftype_path_list[i].lfp_re, + ftype_path_list[i].lfp_re + RE_SIZE, '\0'); + if (rv == NULL) + continue; + + lx_debug("lx_statfs_init compile(\"%s\") failed", + ftype_path_list[i].lfp_path); + return (1); + } + return (0); +} + +static int +stol_type(const char *path, const char *name) +{ + int i; + lx_debug("\tstol_type(\"%s\", \"%s\")\n", path == NULL ? "NULL" : path, + name == NULL ? "NULL" : name); + + if (path != NULL) { + char userpath[MAXPATHLEN]; + + if (uucopystr(path, userpath, MAXPATHLEN) == -1) + return (-errno); + + for (i = 0; ftype_path_list[i].lfp_path != NULL; i++) { + if (step(userpath, ftype_path_list[i].lfp_re) == 0) + continue; + + /* got a match on the fs path */ + lx_debug("\ttranslated f_type to 0x%x - %s", + ftype_path_list[i].lfp_magic, + ftype_path_list[i].lfp_magic_str); + return (ftype_path_list[i].lfp_magic); + } + } + + assert(name != NULL); + for (i = 0; ftype_name_list[i].lfn_name != NULL; i++) { + if (strcmp(name, ftype_name_list[i].lfn_name) == 0) { + + /* got a match on the fs name */ + lx_debug("\ttranslated f_type to 0x%x - %s", + ftype_name_list[i].lfn_magic, + ftype_name_list[i].lfn_magic_str); + return (ftype_name_list[i].lfn_magic); + } + } + + /* we don't know what the fs type is so just set it to 0 */ + return (0); +} + +/* + * The Linux statfs() is similar to the Solaris statvfs() call, the main + * difference being the use of a numeric 'f_type' identifier instead of the + * 'f_basetype' string. + */ +static int +stol_statfs(const char *path, struct lx_statfs *l, struct statvfs *s) +{ + int type; + + if ((type = stol_type(path, s->f_basetype)) < 0) + return (type); + + l->f_type = type; + l->f_bsize = s->f_frsize; /* other fields depend on frsize */ + l->f_blocks = s->f_blocks; + l->f_bfree = s->f_bfree; + l->f_bavail = s->f_bavail; + l->f_files = s->f_files; + l->f_ffree = s->f_ffree; + l->f_fsid = s->f_fsid; + l->f_namelen = s->f_namemax; + l->f_frsize = s->f_frsize; + bzero(&(l->f_spare), sizeof (l->f_spare)); + + return (0); +} + +static int +stol_statfs64(const char *path, struct lx_statfs64 *l, struct statvfs64 *s) +{ + int type; + + if ((type = stol_type(path, s->f_basetype)) < 0) + return (type); + + l->f_type = type; + l->f_bsize = s->f_frsize; /* other fields depend on frsize */ + l->f_blocks = s->f_blocks; + l->f_bfree = s->f_bfree; + l->f_bavail = s->f_bavail; + l->f_files = s->f_files; + l->f_ffree = s->f_ffree; + l->f_fsid = s->f_fsid; + l->f_namelen = s->f_namemax; + l->f_frsize = s->f_frsize; + bzero(&(l->f_spare), sizeof (l->f_spare)); + + return (0); +} + +long +lx_statfs(uintptr_t p1, uintptr_t p2) +{ + const char *path = (const char *)p1; + struct lx_statfs lxfs, *fs = (struct lx_statfs *)p2; + struct statvfs vfs; + int err; + + lx_debug("\tstatvfs(%s, 0x%p)", path, fs); + if (statvfs(path, &vfs) != 0) + return (-errno); + + if ((err = stol_statfs(path, &lxfs, &vfs)) != 0) + return (err); + + if (uucopy(&lxfs, fs, sizeof (struct lx_statfs)) != 0) + return (-errno); + + return (0); +} + +long +lx_fstatfs(uintptr_t p1, uintptr_t p2) +{ + struct lx_statfs lxfs, *fs = (struct lx_statfs *)p2; + struct stat64 sb; + struct statvfs vfs; + char *path, path_buf[MAXPATHLEN]; + int fd = (int)p1; + int err; + + lx_debug("\tfstatvfs(%d, 0x%p)", fd, fs); + + /* + * fstatfs emulation for a pipe. + */ + if (fstat64(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { + lxfs.f_type = LX_PIPEFS_MAGIC; + lxfs.f_bsize = 4096; + lxfs.f_blocks = 0; + lxfs.f_bfree = 0; + lxfs.f_bavail = 0; + lxfs.f_files = 0; + lxfs.f_ffree = 0; + lxfs.f_fsid = 0; + lxfs.f_namelen = 255; + lxfs.f_frsize = 4096; + } else { + if (fstatvfs(fd, &vfs) != 0) + return (-errno); + + path = lx_fd_to_path(fd, path_buf, sizeof (path_buf)); + + if ((err = stol_statfs(path, &lxfs, &vfs)) != 0) + return (err); + } + + if (uucopy(&lxfs, fs, sizeof (struct lx_statfs)) != 0) + return (-errno); + + return (0); +} + +/* ARGSUSED */ +long +lx_statfs64(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + const char *path = (const char *)p1; + struct lx_statfs64 lxfs, *fs = (struct lx_statfs64 *)p3; + struct statvfs64 vfs; + int err; + + lx_debug("\tstatvfs64(%s, %d, 0x%p)", path, p2, fs); + if (statvfs64(path, &vfs) != 0) + return (-errno); + + if ((err = stol_statfs64(path, &lxfs, &vfs)) != 0) + return (err); + + if (uucopy(&lxfs, fs, sizeof (struct lx_statfs64)) != 0) + return (-errno); + + return (0); +} + +/* ARGSUSED */ +long +lx_fstatfs64(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + struct lx_statfs64 lxfs, *fs = (struct lx_statfs64 *)p3; + struct stat64 sb; + struct statvfs64 vfs; + char *path, path_buf[MAXPATHLEN]; + int fd = (int)p1; + int err; + + lx_debug("\tfstatvfs64(%d, %d, 0x%p)", fd, p2, fs); + if (fstat64(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { + lxfs.f_type = LX_PIPEFS_MAGIC; + lxfs.f_bsize = 4096; + lxfs.f_blocks = 0; + lxfs.f_bfree = 0; + lxfs.f_bavail = 0; + lxfs.f_files = 0; + lxfs.f_ffree = 0; + lxfs.f_fsid = 0; + lxfs.f_namelen = 255; + lxfs.f_frsize = 4096; + } else { + if (fstatvfs64(fd, &vfs) != 0) + return (-errno); + + path = lx_fd_to_path(fd, path_buf, sizeof (path_buf)); + + if ((err = stol_statfs64(path, &lxfs, &vfs)) != 0) + return (err); + } + + if (uucopy(&lxfs, fs, sizeof (struct lx_statfs64)) != 0) + return (-errno); + + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/sysctl.c b/usr/src/lib/brand/lx/lx_brand/common/sysctl.c new file mode 100644 index 0000000000..262a9c3830 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/sysctl.c @@ -0,0 +1,137 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2014 Joyent, Inc. All rights reserved. + */ + +#include <alloca.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <sys/lx_syscall.h> +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> + +/* + * sysctl() implementation. The full set of possible values is incredibly + * large; we only implement the bare minimum here, namely basic kernel + * information. + * + * For the moment, we also print out debugging messages if the application + * attempts to write or access any other values, so we can tell if we are not + * supporting something we should be. + */ + +struct lx_sysctl_args { + int *name; + int nlen; + void *oldval; + size_t *oldlenp; + void *newval; + size_t newlen; +}; + +#define LX_CTL_KERN 1 + +#define LX_KERN_OSTYPE 1 +#define LX_KERN_OSRELEASE 2 +#define LX_KERN_OSREV 3 +#define LX_KERN_VERSION 4 + +long +lx_sysctl(uintptr_t raw) +{ + struct lx_sysctl_args args; + int name[2]; + size_t oldlen; + char *namebuf; + + if (uucopy((void *)raw, &args, sizeof (args)) < 0) + return (-EFAULT); + + /* + * We only allow [ CTL_KERN, KERN_* ] pairs, so reject anything that + * doesn't have exactly two values starting with LX_CTL_KERN. + */ + if (args.nlen != 2) + return (-ENOTDIR); + + if (uucopy(args.name, name, sizeof (name)) < 0) + return (-EFAULT); + + if (name[0] != LX_CTL_KERN) { + lx_debug("sysctl: read of [%d, %d] unsupported", + name[0], name[1]); + return (-ENOTDIR); + } + + /* We don't support writing new sysctl values. */ + if ((args.newval != NULL) || (args.newlen != 0)) { + lx_debug("sysctl: write of [%d, %d] unsupported", + name[0], name[1]); + return (-EPERM); + } + + /* + * It may seem silly, but passing in a NULL oldval pointer and not + * writing any new values is a perfectly legal thing to do and should + * succeed. + */ + if (args.oldval == NULL) + return (0); + + /* + * Likewise, Linux specifies that setting a non-NULL oldval but a + * zero *oldlenp should result in an errno of EFAULT. + */ + if ((uucopy(args.oldlenp, &oldlen, sizeof (oldlen)) < 0) || + (oldlen == 0)) + return (-EFAULT); + + namebuf = SAFE_ALLOCA(oldlen); + if (namebuf == NULL) + return (-ENOMEM); + + switch (name[1]) { + case LX_KERN_OSTYPE: + (void) strlcpy(namebuf, LX_UNAME_SYSNAME, oldlen); + break; + case LX_KERN_OSRELEASE: + (void) strlcpy(namebuf, lx_release, oldlen); + break; + case LX_KERN_VERSION: + (void) strlcpy(namebuf, LX_UNAME_VERSION, oldlen); + break; + default: + lx_debug("sysctl: read of [CTL_KERN, %d] unsupported", name[1]); + return (-ENOTDIR); + } + + oldlen = strlen(namebuf); + + if ((uucopy(namebuf, args.oldval, oldlen) < 0) || + (uucopy(&oldlen, args.oldlenp, sizeof (oldlen)) < 0)) + return (-EFAULT); + + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c b/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c new file mode 100644 index 0000000000..aa4f4ffb40 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c @@ -0,0 +1,987 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. All rights reserved. + */ + +#include <errno.h> +#include <unistd.h> +#include <strings.h> +#include <rctl.h> +#include <alloca.h> +#include <values.h> +#include <sys/syscall.h> +#include <sys/msg.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/lx_debug.h> +#include <sys/lx_types.h> +#include <sys/lx_sysv_ipc.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +#define SLOT_SEM 0 +#define SLOT_SHM 1 +#define SLOT_MSG 2 + +/* Use private SHM_RMID interface for IPC_RMID */ +#define SHM_RMID 5 + +static int +get_rctlval(rctlblk_t *rblk, char *name, ulong_t limit, uint64_t *val) +{ + rctl_qty_t r; + + if (getrctl(name, NULL, rblk, RCTL_FIRST) == -1) + return (-errno); + + r = rctlblk_get_value(rblk); + if (r > limit) + return (-EOVERFLOW); + + *val = r; + return (0); +} + +/* + * Given a slot number and a maximum number of ids to extract from the + * kernel, return the msgid in the provided slot. + */ +static int +slot_to_id(int type, int slot) +{ + uint_t nids, max; + int *idbuf = NULL; + int r = 0; + + nids = 0; + for (;;) { + switch (type) { + case SLOT_SEM: + r = semids(idbuf, nids, &max); + break; + case SLOT_SHM: + r = shmids(idbuf, nids, &max); + break; + case SLOT_MSG: + r = msgids(idbuf, nids, &max); + break; + } + + if (r < 0) + return (-errno); + + if (max == 0) + return (-EINVAL); + + if (max <= nids) + return (idbuf[slot]); + + nids = max; + if ((idbuf = (int *)SAFE_ALLOCA(sizeof (int) * nids)) == NULL) + return (-ENOMEM); + } +} + +/* + * Semaphore operations. + */ +long +lx_semget(key_t key, int nsems, int semflg) +{ + int sol_flag; + int r; + + lx_debug("\nsemget(%d, %d, %d)\n", key, nsems, semflg); + sol_flag = semflg & S_IAMB; + if (semflg & LX_IPC_CREAT) + sol_flag |= IPC_CREAT; + if (semflg & LX_IPC_EXCL) + sol_flag |= IPC_EXCL; + + r = semget(key, nsems, sol_flag); + return ((r < 0) ? -errno : r); +} + +long +lx_semop(int semid, void *p1, size_t nsops) +{ + int r; + struct sembuf *sops = (struct sembuf *)p1; + + lx_debug("\nsemop(%d, 0x%p, %u)\n", semid, sops, nsops); + if (nsops == 0) + return (-EINVAL); + + r = semop(semid, sops, nsops); + return ((r < 0) ? -errno : r); +} + +long +lx_semtimedop(int semid, void *p1, size_t nsops, struct timespec *timeout) +{ + int r; + struct sembuf *sops = (struct sembuf *)p1; + + lx_debug("\nsemtimedop(%d, 0x%p, %u, 0x%p)\n", semid, sops, nsops, + timeout); + if (nsops == 0) + return (-EINVAL); + + r = semtimedop(semid, sops, nsops, timeout); + return ((r < 0) ? -errno : r); +} + +static int +lx_semctl_ipcset(int semid, void *buf) +{ + struct lx_semid_ds semds; + struct semid_ds sol_semds; + int r; + + if (uucopy(buf, &semds, sizeof (semds))) + return (-errno); + + bzero(&sol_semds, sizeof (sol_semds)); + sol_semds.sem_perm.uid = semds.sem_perm.uid; + sol_semds.sem_perm.gid = semds.sem_perm.gid; + sol_semds.sem_perm.mode = semds.sem_perm.mode; + + r = semctl(semid, 0, IPC_SET, &sol_semds); + return ((r < 0) ? -errno : r); +} + +static int +lx_semctl_ipcstat(int semid, void *buf) +{ + struct lx_semid_ds semds; + struct semid_ds sol_semds; + + if (semctl(semid, 0, IPC_STAT, &sol_semds) != 0) + return (-errno); + + bzero(&semds, sizeof (semds)); + semds.sem_perm.key = sol_semds.sem_perm.key; + semds.sem_perm.seq = sol_semds.sem_perm.seq; + semds.sem_perm.uid = sol_semds.sem_perm.uid; + semds.sem_perm.gid = sol_semds.sem_perm.gid; + semds.sem_perm.cuid = sol_semds.sem_perm.cuid; + semds.sem_perm.cgid = sol_semds.sem_perm.cgid; + + /* Linux only uses the bottom 9 bits */ + semds.sem_perm.mode = sol_semds.sem_perm.mode & S_IAMB; + semds.sem_otime = sol_semds.sem_otime; + semds.sem_ctime = sol_semds.sem_ctime; + semds.sem_nsems = sol_semds.sem_nsems; + + if (uucopy(&semds, buf, sizeof (semds))) + return (-errno); + + return (0); +} + +static int +lx_semctl_ipcinfo(void *buf) +{ + struct lx_seminfo i; + rctlblk_t *rblk; + int rblksz; + uint_t nids; + int idbuf; + int err; + uint64_t val; + + rblksz = rctlblk_size(); + if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL) + return (-ENOMEM); + + bzero(&i, sizeof (i)); + err = get_rctlval(rblk, "project.max-sem-ids", (ulong_t)MAXINT, &val); + if (err < 0) + return (err); + i.semmni = (int)val; + err = get_rctlval(rblk, "process.max-sem-nsems", (ulong_t)MAXINT, &val); + if (err < 0) + return (err); + i.semmsl = (int)val; + err = get_rctlval(rblk, "process.max-sem-ops", (ulong_t)MAXINT, &val); + if (err < 0) + return (err); + i.semopm = (int)val; + + /* + * We don't have corresponding rctls for these fields. The values + * are taken from the formulas used to derive the defaults listed + * in the Linux header file. We're lying, but trying to be + * coherent about it. + */ + i.semmap = i.semmni; + i.semmns = i.semmni * i.semmsl; + i.semmnu = INT_MAX; + i.semume = INT_MAX; + i.semvmx = LX_SEMVMX; + if (semids(&idbuf, 0, &nids) < 0) + return (-errno); + i.semusz = nids; + i.semaem = INT_MAX; + + if (uucopy(&i, buf, sizeof (i)) != 0) + return (-errno); + + return (nids); +} + +static int +lx_semctl_semstat(int slot, void *buf) +{ + int r, semid; + + semid = slot_to_id(SLOT_SEM, slot); + if (semid < 0) + return (semid); + + r = lx_semctl_ipcstat(semid, buf); + return (r < 0 ? r : semid); +} + +/* + * For the SETALL operation, we have to examine each of the semaphore + * values to be sure it is legal. + */ +static int +lx_semctl_setall(int semid, ushort_t *arg) +{ + struct semid_ds semds; + ushort_t *vals; + int i, sz, r; + + /* + * Find out how many semaphores are involved, reserve enough + * memory for an internal copy of the array, and then copy it in + * from the process. + */ + if (semctl(semid, 0, IPC_STAT, &semds) != 0) + return (-errno); + sz = semds.sem_nsems * sizeof (ushort_t); + if ((vals = SAFE_ALLOCA(sz)) == NULL) + return (-ENOMEM); + if (uucopy(arg, vals, sz)) + return (-errno); + + /* Validate each of the values. */ + for (i = 0; i < semds.sem_nsems; i++) + if (vals[i] > LX_SEMVMX) + return (-ERANGE); + + r = semctl(semid, 0, SETALL, arg); + + return ((r < 0) ? -errno : r); +} + +long +lx_semctl(int semid, int semnum, int cmd, void *ptr) +{ +#if defined(_ILP32) + union lx_semun arg; +#endif + int rval; + int opt = cmd & ~LX_IPC_64; + int use_errno = 0; + uint_t val; + + lx_debug("\nsemctl(%d, %d, %d, 0x%p)\n", semid, semnum, cmd, ptr); + +#if defined(_ILP32) + /* + * The final arg to semctl() is a pointer to a union. For some + * commands we can hand that pointer directly to the kernel. For + * these commands, we need to extract an argument from the union + * before calling into the kernel. + */ + if (opt == LX_SETVAL || opt == LX_SETALL || opt == LX_GETALL || + opt == LX_IPC_SET || opt == LX_IPC_STAT || opt == LX_SEM_STAT || + opt == LX_IPC_INFO || opt == LX_SEM_INFO) + if (uucopy(ptr, &arg, sizeof (arg))) + return (-errno); +#endif + + switch (opt) { + case LX_GETVAL: + use_errno = 1; + rval = semctl(semid, semnum, GETVAL, NULL); + break; + case LX_SETVAL: +#if defined(_ILP32) + val = arg.val; +#else + val = (uint_t)(uintptr_t)ptr; +#endif + if (val > LX_SEMVMX) { + return (-ERANGE); + } + use_errno = 1; + rval = semctl(semid, semnum, SETVAL, val); + break; + case LX_GETPID: + use_errno = 1; + rval = semctl(semid, semnum, GETPID, NULL); + break; + case LX_GETNCNT: + use_errno = 1; + rval = semctl(semid, semnum, GETNCNT, NULL); + break; + case LX_GETZCNT: + use_errno = 1; + rval = semctl(semid, semnum, GETZCNT, NULL); + break; + case LX_GETALL: + use_errno = 1; +#if defined(_ILP32) + rval = semctl(semid, semnum, GETALL, arg.sems); +#else + rval = semctl(semid, semnum, GETALL, ptr); +#endif + break; + case LX_SETALL: +#if defined(_ILP32) + rval = lx_semctl_setall(semid, arg.sems); +#else + rval = lx_semctl_setall(semid, ptr); +#endif + break; + case LX_IPC_RMID: + use_errno = 1; + rval = semctl(semid, semnum, IPC_RMID, NULL); + break; + case LX_SEM_STAT: +#if defined(_ILP32) + rval = lx_semctl_semstat(semid, arg.semds); +#else + rval = lx_semctl_semstat(semid, ptr); +#endif + break; + case LX_IPC_STAT: +#if defined(_ILP32) + rval = lx_semctl_ipcstat(semid, arg.semds); +#else + rval = lx_semctl_ipcstat(semid, ptr); +#endif + break; + + case LX_IPC_SET: +#if defined(_ILP32) + rval = lx_semctl_ipcset(semid, arg.semds); +#else + rval = lx_semctl_ipcset(semid, ptr); +#endif + break; + + case LX_IPC_INFO: + case LX_SEM_INFO: +#if defined(_ILP32) + rval = lx_semctl_ipcinfo(arg.semds); +#else + rval = lx_semctl_ipcinfo(ptr); +#endif + break; + + default: + return (-EINVAL); + } + + if (use_errno == 1 && rval < 0) + return (-errno); + return (rval); +} + +/* + * msg operations. + */ +long +lx_msgget(key_t key, int flag) +{ + int sol_flag; + int r; + + lx_debug("\tlx_msgget(%d, %d)\n", key, flag); + + sol_flag = flag & S_IAMB; + if (flag & LX_IPC_CREAT) + sol_flag |= IPC_CREAT; + if (flag & LX_IPC_EXCL) + sol_flag |= IPC_EXCL; + + r = msgget(key, sol_flag); + return (r < 0 ? -errno : r); +} + +long +lx_msgsnd(int id, void *p1, size_t sz, int flag) +{ + int sol_flag = 0; + int r; + struct msgbuf *buf = (struct msgbuf *)p1; + + lx_debug("\tlx_msgsnd(%d, 0x%p, %d, %d)\n", id, buf, sz, flag); + + if (flag & LX_IPC_NOWAIT) + sol_flag |= IPC_NOWAIT; + + if (((ssize_t)sz < 0) || (sz > LX_MSGMAX)) + return (-EINVAL); + + r = msgsnd(id, buf, sz, sol_flag); + return (r < 0 ? -errno : r); +} + +long +lx_msgrcv(int id, void *msgp, size_t sz, long msgtype, int flag) +{ + int sol_flag = 0; + ssize_t r; + + lx_debug("\tlx_msgrcv(%d, 0x%p, %d, %d, %ld, %d)\n", + id, msgp, sz, msgtype, flag); + + /* + * Check for a negative sz parameter. + * + * Unlike msgsnd(2), the Linux man page does not specify that + * msgrcv(2) should return EINVAL if (sz > MSGMAX), only if (sz < 0). + */ + if ((ssize_t)sz < 0) + return (-EINVAL); + + if (flag & LX_MSG_NOERROR) + sol_flag |= MSG_NOERROR; + if (flag & LX_IPC_NOWAIT) + sol_flag |= IPC_NOWAIT; + + r = msgrcv(id, msgp, sz, msgtype, sol_flag); + return (r < 0 ? -errno : r); +} + +static int +lx_msgctl_ipcstat(int msgid, void *buf) +{ + struct lx_msqid_ds msgids; + struct msqid_ds sol_msgids; + int r; + + r = msgctl(msgid, IPC_STAT, &sol_msgids); + if (r < 0) + return (-errno); + + bzero(&msgids, sizeof (msgids)); + msgids.msg_perm.key = sol_msgids.msg_perm.key; + msgids.msg_perm.seq = sol_msgids.msg_perm.seq; + msgids.msg_perm.uid = sol_msgids.msg_perm.uid; + msgids.msg_perm.gid = sol_msgids.msg_perm.gid; + msgids.msg_perm.cuid = sol_msgids.msg_perm.cuid; + msgids.msg_perm.cgid = sol_msgids.msg_perm.cgid; + + /* Linux only uses the bottom 9 bits */ + msgids.msg_perm.mode = sol_msgids.msg_perm.mode & S_IAMB; + + msgids.msg_stime = sol_msgids.msg_stime; + msgids.msg_rtime = sol_msgids.msg_rtime; + msgids.msg_ctime = sol_msgids.msg_ctime; + msgids.msg_qbytes = sol_msgids.msg_qbytes; + msgids.msg_cbytes = sol_msgids.msg_cbytes; + msgids.msg_qnum = sol_msgids.msg_qnum; + msgids.msg_lspid = sol_msgids.msg_lspid; + msgids.msg_lrpid = sol_msgids.msg_lrpid; + + if (uucopy(&msgids, buf, sizeof (msgids))) + return (-errno); + + return (0); +} + +static int +lx_msgctl_ipcinfo(int cmd, void *buf) +{ + struct lx_msginfo m; + rctlblk_t *rblk; + int idbuf, rblksz, msgseg, maxmsgs; + uint_t nids; + int rval; + int err; + uint64_t val; + + rblksz = rctlblk_size(); + if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL) + return (-ENOMEM); + + bzero(&m, sizeof (m)); + err = get_rctlval(rblk, "project.max-msg-ids", (ulong_t)MAXINT, &val); + if (err < 0) + return (err); + m.msgmni = (int)val; + err = get_rctlval(rblk, "process.max-msg-qbytes", (ulong_t)MAXINT, + &val); + if (err < 0) + return (err); + m.msgmnb = (int)val; + + if (cmd == LX_IPC_INFO) { + err = get_rctlval(rblk, "process.max-msg-messages", + (ulong_t)MAXINT, &val); + if (err < 0) + return (err); + maxmsgs = (int)val; + m.msgtql = maxmsgs * m.msgmni; + m.msgmap = m.msgmnb; + m.msgpool = m.msgmax * m.msgmnb; + rval = 0; + } else { + if (msgids(&idbuf, 0, &nids) < 0) + return (-errno); + m.msgpool = nids; + + /* + * For these fields, we can't even come up with a good fake + * approximation. These are listed as 'obsolete' or + * 'unused' in the header files, so hopefully nobody is + * relying on them anyway. + */ + m.msgtql = INT_MAX; + m.msgmap = INT_MAX; + rval = nids; + } + + /* + * We don't have corresponding rctls for these fields. The values + * are taken from the formulas used to derive the defaults listed + * in the Linux header file. We're lying, but trying to be + * coherent about it. + */ + m.msgmax = LX_MSGMAX; + m.msgssz = 16; + msgseg = (m.msgpool * 1024) / m.msgssz; + m.msgseg = (msgseg > 0xffff) ? 0xffff : msgseg; + + if (uucopy(&m, buf, sizeof (m))) + return (-errno); + return (rval); +} + +static int +lx_msgctl_ipcset(int msgid, void *buf) +{ + struct lx_msqid_ds msgids; + struct msqid_ds sol_msgids; + int r; + + if (uucopy(buf, &msgids, sizeof (msgids))) + return (-errno); + + bzero(&sol_msgids, sizeof (sol_msgids)); + sol_msgids.msg_perm.uid = LX_UID16_TO_UID32(msgids.msg_perm.uid); + sol_msgids.msg_perm.gid = LX_UID16_TO_UID32(msgids.msg_perm.gid); + + /* Linux only uses the bottom 9 bits */ + sol_msgids.msg_perm.mode = msgids.msg_perm.mode & S_IAMB; + sol_msgids.msg_qbytes = msgids.msg_qbytes; + + r = msgctl(msgid, IPC_SET, &sol_msgids); + return (r < 0 ? -errno : r); +} + +static int +lx_msgctl_msgstat(int slot, void *buf) +{ + int r, msgid; + + lx_debug("msgstat(%d, 0x%p)\n", slot, buf); + + msgid = slot_to_id(SLOT_MSG, slot); + + if (msgid < 0) + return (msgid); + + r = lx_msgctl_ipcstat(msgid, buf); + return (r < 0 ? r : msgid); +} + +/* + * Split off the various msgctl's here + */ +long +lx_msgctl(int msgid, int cmd, void *buf) +{ + int r; + + lx_debug("\tlx_msgctl(%d, %d, 0x%p)\n", msgid, cmd, buf); + switch (cmd & ~LX_IPC_64) { + case LX_IPC_RMID: + r = msgctl(msgid, IPC_RMID, NULL); + if (r < 0) + r = -errno; + break; + case LX_IPC_SET: + r = lx_msgctl_ipcset(msgid, buf); + break; + case LX_IPC_STAT: + r = lx_msgctl_ipcstat(msgid, buf); + break; + case LX_MSG_STAT: + r = lx_msgctl_msgstat(msgid, buf); + break; + + case LX_IPC_INFO: + case LX_MSG_INFO: + r = lx_msgctl_ipcinfo(cmd, buf); + break; + + default: + r = -EINVAL; + break; + } + + return (r); +} + +/* + * shm-related operations. + */ +long +lx_shmget(key_t key, size_t size, int flag) +{ + int sol_flag; + int r; + + lx_debug("\tlx_shmget(%d, %d, %d)\n", key, size, flag); + + sol_flag = flag & S_IAMB; + if (flag & LX_IPC_CREAT) + sol_flag |= IPC_CREAT; + if (flag & LX_IPC_EXCL) + sol_flag |= IPC_EXCL; + + r = shmget(key, size, sol_flag); + return (r < 0 ? -errno : r); +} + +long +lx_shmat(int shmid, void *addr, int flags) +{ + int sol_flags; + void *ptr; + + lx_debug("\tlx_shmat(%d, 0x%p, %d)\n", shmid, addr, flags); + + /* + * Linux has a fix for CVE-2017-5669 which LTP is testing for. The + * kernel will disallow mapping into the first 64k of the address space. + * LTP passes 1 as the address which will then round down to 0. + * In the future, once more work has been done to tighten up the lx + * brand handling for the minimum mappable address (e.g. with secflags), + * then we can remove this check. + */ + if ((flags & LX_SHM_RND) && addr != NULL && addr < (void *)0x10000) { + return (-EINVAL); + } + + sol_flags = 0; + if (flags & LX_SHM_RDONLY) + sol_flags |= SHM_RDONLY; + if (flags & LX_SHM_RND) + sol_flags |= SHM_RND; + if ((flags & LX_SHM_REMAP) && (addr == NULL)) + return (-EINVAL); + + ptr = shmat(shmid, addr, sol_flags); + if (ptr == (void *)-1) + return (-errno); + + return ((ssize_t)ptr); +} + +static int +lx_shmctl_ipcinfo(void *buf) +{ + struct lx_shminfo s; + rctlblk_t *rblk; + int rblksz; + int err; + uint64_t val; + + rblksz = rctlblk_size(); + if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL) + return (-ENOMEM); + + bzero(&s, sizeof (s)); + err = get_rctlval(rblk, "project.max-shm-ids", ULONG_MAX, &val); + if (err < 0) + return (err); + s.shmmni = val; + err = get_rctlval(rblk, "project.max-shm-memory", ULONG_MAX, &val); + if (err < 0) + return (err); + s.shmmax = val; + + /* + * We don't have corresponding rctls for these fields. The values + * are taken from the formulas used to derive the defaults listed + * in the Linux header file. We're lying, but trying to be + * coherent about it. + */ + s.shmmin = 1; + s.shmseg = ULONG_MAX; + s.shmall = s.shmmax / getpagesize(); + + if (uucopy(&s, buf, sizeof (s))) + return (-errno); + + return (0); +} + +static int +lx_shmctl_ipcstat(int shmid, void *buf) +{ + struct lx_shmid_ds shmds; + struct shmid_ds sol_shmds; + + if (shmctl(shmid, IPC_STAT, &sol_shmds) != 0) + return (-errno); + + bzero(&shmds, sizeof (shmds)); + shmds.shm_perm.key = sol_shmds.shm_perm.key; + shmds.shm_perm.seq = sol_shmds.shm_perm.seq; + shmds.shm_perm.uid = sol_shmds.shm_perm.uid; + shmds.shm_perm.gid = sol_shmds.shm_perm.gid; + shmds.shm_perm.cuid = sol_shmds.shm_perm.cuid; + shmds.shm_perm.cgid = sol_shmds.shm_perm.cgid; + shmds.shm_perm.mode = sol_shmds.shm_perm.mode & S_IAMB; + if (sol_shmds.shm_lkcnt > 0) + shmds.shm_perm.mode |= LX_SHM_LOCKED; + shmds.shm_segsz = sol_shmds.shm_segsz; + shmds.shm_atime = sol_shmds.shm_atime; + shmds.shm_dtime = sol_shmds.shm_dtime; + shmds.shm_ctime = sol_shmds.shm_ctime; + shmds.shm_cpid = sol_shmds.shm_cpid; + shmds.shm_lpid = sol_shmds.shm_lpid; + shmds.shm_nattch = (ushort_t)sol_shmds.shm_nattch; + + if (uucopy(&shmds, buf, sizeof (shmds))) + return (-errno); + + return (0); +} + +static int +lx_shmctl_ipcset(int shmid, void *buf) +{ + struct lx_shmid_ds shmds; + struct shmid_ds sol_shmds; + int r; + + if (uucopy(buf, &shmds, sizeof (shmds))) + return (-errno); + + bzero(&sol_shmds, sizeof (sol_shmds)); + sol_shmds.shm_perm.uid = shmds.shm_perm.uid; + sol_shmds.shm_perm.gid = shmds.shm_perm.gid; + sol_shmds.shm_perm.mode = shmds.shm_perm.mode & S_IAMB; + + r = shmctl(shmid, IPC_SET, &sol_shmds); + return (r < 0 ? -errno : r); +} + +/* + * Build and return a shm_info structure. We only return the bare + * essentials required by ipcs. The rest of the info is not readily + * available. + */ +static int +lx_shmctl_shminfo(void *buf) +{ + struct lx_shm_info shminfo; + uint_t nids; + int idbuf; + + bzero(&shminfo, sizeof (shminfo)); + + if (shmids(&idbuf, 0, &nids) < 0) + return (-errno); + + shminfo.used_ids = nids; + if (uucopy(&shminfo, buf, sizeof (shminfo)) != 0) + return (-errno); + + return (nids); +} + +static int +lx_shmctl_shmstat(int slot, void *buf) +{ + int r, shmid; + + lx_debug("shmctl_shmstat(%d, 0x%p)\n", slot, buf); + shmid = slot_to_id(SLOT_SHM, slot); + if (shmid < 0) + return (shmid); + + r = lx_shmctl_ipcstat(shmid, buf); + return (r < 0 ? r : shmid); +} + +long +lx_shmctl(int shmid, int cmd, void *buf) +{ + int r; + int use_errno = 0; + + lx_debug("\tlx_shmctl(%d, %d, 0x%p)\n", shmid, cmd, buf); + switch (cmd & ~LX_IPC_64) { + case LX_IPC_RMID: + use_errno = 1; + r = shmctl(shmid, SHM_RMID, NULL); /* lx-private cmd */ + break; + + case LX_IPC_SET: + r = lx_shmctl_ipcset(shmid, buf); + break; + + case LX_IPC_STAT: + r = lx_shmctl_ipcstat(shmid, buf); + break; + + case LX_IPC_INFO: + r = lx_shmctl_ipcinfo(buf); + break; + + case LX_SHM_LOCK: + use_errno = 1; + r = shmctl(shmid, SHM_LOCK, NULL); + break; + + case LX_SHM_UNLOCK: + use_errno = 1; + r = shmctl(shmid, SHM_UNLOCK, NULL); + break; + + case LX_SHM_INFO: + r = lx_shmctl_shminfo(buf); + break; + + case LX_SHM_STAT: + r = lx_shmctl_shmstat(shmid, buf); + break; + default: + r = -EINVAL; + break; + } + + if (use_errno == 1 && r < 0) + return (-errno); + + return (r); +} + +/* + * Under 32-bit Linux, glibc funnels all of the sysv IPC operations into this + * single ipc(2) system call. We need to blow that up and filter the + * remnants into the proper Solaris system calls. + */ +long +lx_ipc(uintptr_t cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, + uintptr_t arg4) +{ + int r; + void *bufptr = (void *)arg4; + + lx_debug("lx_ipc(%d, %d, %d, %d, 0x%p, %d)\n", + cmd, arg1, arg2, arg3, bufptr, arg4); + + switch (cmd) { + case LX_MSGGET: + r = lx_msgget((key_t)arg1, (int)arg2); + break; + case LX_MSGSND: + r = lx_msgsnd((int)arg1, bufptr, (size_t)arg2, (int)arg3); + break; + case LX_MSGRCV: + { + struct { + void *msgp; + long msgtype; + } args; + + /* + * Rather than passing 5 args into ipc(2) directly, + * glibc passes 4 args and uses the buf argument to + * point to a structure containing two args: a pointer + * to the message and the message type. + */ + if (uucopy(bufptr, &args, sizeof (args))) + return (-errno); + r = lx_msgrcv((int)arg1, args.msgp, (size_t)arg2, + args.msgtype, (int)arg3); + } + break; + case LX_MSGCTL: + r = lx_msgctl((int)arg1, (int)arg2, bufptr); + break; + case LX_SEMCTL: + r = lx_semctl((int)arg1, (size_t)arg2, (int)arg3, bufptr); + break; + case LX_SEMOP: + /* + * 'struct sembuf' is the same on Linux and Solaris, so we + * pass bufptr straight through. + */ + r = lx_semop((int)arg1, bufptr, (size_t)arg2); + break; + case LX_SEMGET: + r = lx_semget((int)arg1, (size_t)arg2, (int)arg3); + break; + case LX_SHMAT: + r = lx_shmat((int)arg1, bufptr, (size_t)arg2); + if (r >= 0 || r <= -4096) { + if (uucopy(&r, (void *)arg3, sizeof (r)) != 0) + r = -errno; + } + break; + case LX_SHMDT: + r = shmdt(bufptr); + if (r < 0) + r = -errno; + break; + case LX_SHMGET: + r = lx_shmget((int)arg1, (size_t)arg2, (int)arg3); + break; + case LX_SHMCTL: + r = lx_shmctl((int)arg1, (int)arg2, bufptr); + break; + + default: + r = -EINVAL; + } + + return (r); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/time.c b/usr/src/lib/brand/lx/lx_brand/common/time.c new file mode 100644 index 0000000000..9b82ec98b5 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/time.c @@ -0,0 +1,113 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. + */ + +#include <errno.h> +#include <time.h> +#include <string.h> +#include <strings.h> +#include <sys/times.h> +#include <sys/lx_syscall.h> +#include <sys/lx_misc.h> + +/* + * setitimer() - the Linux implementation can handle tv_usec values greater + * than 1,000,000 where Illumos would return EINVAL. + * + * There's still an issue here where Linux can handle a + * tv_sec value greater than 100,000,000 but Illumos cannot, + * but that would also mean setting an interval timer to fire + * over _three years_ in the future so it's unlikely anything + * other than Linux test suites will trip over it. + */ +long +lx_setitimer(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + struct itimerval itv; + struct itimerval *itp = (struct itimerval *)p2; + + if (itp != NULL) { + if (uucopy(itp, &itv, sizeof (itv)) != 0) + return (-errno); + + /* + * Adjust any tv_usec fields >= 1,000,000 by adding any whole + * seconds so indicated to tv_sec and leaving tv_usec as the + * remainder. + */ + if (itv.it_interval.tv_usec >= MICROSEC) { + itv.it_interval.tv_sec += + itv.it_interval.tv_usec / MICROSEC; + + itv.it_interval.tv_usec %= MICROSEC; + } + if (itv.it_value.tv_usec >= MICROSEC) { + itv.it_value.tv_sec += + itv.it_value.tv_usec / MICROSEC; + + itv.it_value.tv_usec %= MICROSEC; + } + + itp = &itv; + } + + return ((setitimer((int)p1, itp, (struct itimerval *)p3) != 0) ? + -errno : 0); +} + +/* + * NOTE: The Linux man pages state this structure is obsolete and is + * unsupported, so it is declared here for sizing purposes only. + */ +struct lx_timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +long +lx_settimeofday(uintptr_t p1, uintptr_t p2) +{ + struct timeval tv; + struct lx_timezone tz; + + if ((p1 != (uintptr_t)NULL) && + (uucopy((struct timeval *)p1, &tv, sizeof (tv)) < 0)) + return (-errno); + + /* + * The Linux man page states use of the second parameter is obsolete, + * but settimeofday(2) should still return EFAULT if it is set + * to a bad non-NULL pointer (sigh...) + */ + if ((p2 != (uintptr_t)NULL) && + (uucopy((struct lx_timezone *)p2, &tz, sizeof (tz)) < 0)) + return (-errno); + + if ((p1 != (uintptr_t)NULL) && (settimeofday(&tv, NULL) < 0)) + return (-errno); + + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/truncate.c b/usr/src/lib/brand/lx/lx_brand/common/truncate.c new file mode 100644 index 0000000000..ba3e452408 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/truncate.c @@ -0,0 +1,173 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2014 Joyent, Inc. All rights reserved. + */ + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <sys/lx_types.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +/* + * ZFS does not enforce the process.max-file-size rctl on a file which is + * grown in size via truncate/ftruncate, since that is simply metadata which + * does not consume any additional space. However, LTP truncate03 depends on + * this behavior so we enforce it here. + */ +static boolean_t +p_fsize_excd(const char *path, off_t length) +{ + struct stat64 sb; + struct rlimit rl; + + if (stat64(path, &sb) == 0 && sb.st_size < length) { + /* We are growing the file, check the rlimit */ + if (getrlimit(RLIMIT_FSIZE, &rl) == 0 && length > rl.rlim_cur) + return (B_TRUE); + } + + return (B_FALSE); +} + +static boolean_t +f_fsize_excd(int fd, off_t length) +{ + struct stat64 sb; + struct rlimit rl; + + if (fstat64(fd, &sb) == 0 && sb.st_size < length) { + /* We are growing the file, check the rlimit */ + if (getrlimit(RLIMIT_FSIZE, &rl) == 0 && length > rl.rlim_cur) + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * On Illumos, truncate() and ftruncate() are implemented in libc, so these are + * layered on those interfaces. + */ + +long +lx_truncate(uintptr_t path, uintptr_t length) +{ +#if defined(_ILP32) + if ((off_t)length >= 0xffffffffUL) + return (-EFBIG); +#endif + + if (length > 0 && p_fsize_excd((const char *)path, (off_t)length)) + return (-EFBIG); + + return (truncate((const char *)path, (off_t)length) == 0 ? 0 : -errno); +} + +long +lx_ftruncate(uintptr_t fd, uintptr_t length) +{ + int r; + +#if defined(_ILP32) + if ((off_t)length >= 0xffffffffUL) + return (-EFBIG); +#endif + + if (length > 0 && f_fsize_excd((int)fd, (off_t)length)) + return (-EFBIG); + + r = ftruncate((int)fd, (off_t)length); + /* + * On Linux, truncating a file opened read-only returns EINVAL whereas + * Illumos returns EBADF. + */ + if (r != 0) { + if (errno == EBADF) { + int mode; + + if ((mode = fcntl(fd, F_GETFL, 0)) != -1 && + (mode & O_ACCMODE) == O_RDONLY) + r = -EINVAL; + else + r = -EBADF; /* keep existing errno */ + } else { + r = -errno; + } + } + return (r); +} + +long +lx_truncate64(uintptr_t path, uintptr_t length_lo, uintptr_t length_hi) +{ + uint64_t len = LX_32TO64(length_lo, length_hi); + + if (len >= 0x7fffffffffffffffULL) + return (-EFBIG); + + if (len > 0 && p_fsize_excd((const char *)path, (off_t)len)) + return (-EFBIG); + + return (truncate64((const char *)path, len) == 0 ? 0 : -errno); +} + +long +lx_ftruncate64(uintptr_t fd, uintptr_t length_lo, uintptr_t length_hi) +{ + int r; + uint64_t len = LX_32TO64(length_lo, length_hi); + + if (len >= 0x7fffffffffffffffULL) + return (-EFBIG); + + if (len > 0 && f_fsize_excd((int)fd, (off_t)len)) + return (-EFBIG); + + r = ftruncate64((int)fd, len); + /* + * On Linux, truncating a file opened read-only returns EINVAL whereas + * Illumos returns EBADF. + */ + if (r != 0) { + if (errno == EBADF) { + int mode; + + if ((mode = fcntl(fd, F_GETFL, 0)) != -1 && + (mode & O_ACCMODE) == O_RDONLY) + r = -EINVAL; + else + r = -EBADF; /* keep existing errno */ + } else { + r = -errno; + } + } + + return (r); +} diff --git a/usr/src/lib/brand/lx/lx_brand/i386/Makefile b/usr/src/lib/brand/lx/lx_brand/i386/Makefile new file mode 100644 index 0000000000..8723f64292 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/i386/Makefile @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2015 Joyent, Inc. +# +# lib/brand/lx/i386/Makefile + +ISASRCDIR=. + +ASFLAGS += -P -D_ASM + +include ../Makefile.com + +DYNFLAGS += -Wl,-I/native/lib/ld.so.1 + +POFILE= lx_brand.po +MSGFILES= $(CSRCS) + +ASSYMDEP_OBJS = lx_handler.o + +install: all $(ROOTLIBS) + +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) + +_msg: $(MSGDOMAINPOFILE) + +include $(SRC)/Makefile.msg.targ diff --git a/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s b/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s new file mode 100644 index 0000000000..c457c1c209 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ident "%Z%%M% %I% %E% SMI" + +#include <sys/asm_linkage.h> + +#if defined(lint) + +void +_start(void) +{ +} + +#else /* lint */ + + /* + * C language startup routine for the lx brand shared library. + */ + ENTRY_NP(_start) + pushl $0 / Build a stack frame. retpc = NULL + pushl $0 / fp = NULL + movl %esp, %ebp / first stack frame + + /* + * Calculate the location of the envp array by adding the size of + * the argv array to the start of the argv array. + */ + movl 8(%ebp), %eax / argc in %eax + leal 16(%ebp,%eax,4), %edx / envp in %edx + andl $-16, %esp + pushl %edx / push envp + leal 12(%ebp),%edx / compute &argv[0] + pushl %edx / push argv + pushl %eax / push argc + call lx_init + /* + * lx_init will never return. + */ + SET_SIZE(_start) + +#endif /* lint */ diff --git a/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s b/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s new file mode 100644 index 0000000000..7afb9c4b7f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s @@ -0,0 +1,91 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#include <sys/asm_linkage.h> +#include <sys/regset.h> +#include <sys/segments.h> +#include <sys/syscall.h> +#include <sys/lx_brand.h> + +#if defined(_ASM) +#include <sys/lx_signal.h> +#include <sys/lx_syscall.h> +#endif /* _ASM */ + +/* 32-bit syscall numbers */ +#define LX_SYS_sigreturn 119 +#define LX_SYS_rt_sigreturn 173 + +#if defined(lint) + +#include <sys/types.h> +#include <sys/regset.h> +#include <sys/signal.h> + +void +lx_sigreturn_tramp(void) +{} + +void +lx_rt_sigreturn_tramp(void) +{} + +#else /* lint */ + + ENTRY_NP(lx_swap_gs) + push %eax /* save the current eax value */ + movl 0xc(%esp),%eax /* 2nd param is a pointer */ + movw %gs,(%eax) /* use the pointer to save current gs */ + movl 0x8(%esp),%eax /* first parameter is the new gs value */ + movw %ax, %gs /* switch to the new gs value */ + pop %eax /* restore eax */ + ret + SET_SIZE(lx_swap_gs) + + /* + * Trampoline code is called by the return at the end of a Linux + * signal handler to return control to the interrupted application + * via the lx_sigreturn() or lx_rt_sigreturn() syscalls. + * + * (lx_sigreturn() is called for legacy signal handling, and + * lx_rt_sigreturn() is called for "new"-style signals.) + * + * These two routines must consist of the EXACT code sequences below + * as gdb looks at the sequence of instructions a routine will return + * to determine whether it is in a signal handler or not. + * See the Linux code setup_signal_stack_sc() in arch/x86/um/signal.c. + */ + ENTRY_NP(lx_sigreturn_tramp) + popl %eax + movl $LX_SYS_sigreturn, %eax + int $0x80 + SET_SIZE(lx_sigreturn_tramp) + + ENTRY_NP(lx_rt_sigreturn_tramp) + movl $LX_SYS_rt_sigreturn, %eax + int $0x80 + SET_SIZE(lx_rt_sigreturn_tramp) +#endif /* lint */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h new file mode 100644 index 0000000000..18e452876a --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h @@ -0,0 +1,54 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +#ifndef _LX_DEBUG_H +#define _LX_DEBUG_H + +#include <lx_provider_impl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* initialize the debugging subsystem */ +extern void lx_debug_init(boolean_t, boolean_t, const char *); + +/* printf() style debug message functionality */ +extern void lx_debug(const char *, ...); + +extern int lx_debug_enabled; + +#define LX_DEBUG_ISENABLED \ + (lx_debug_enabled || LX_DEBUG_ENABLED()) + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_DEBUG_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h new file mode 100644 index 0000000000..455ac174df --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h @@ -0,0 +1,179 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +#ifndef _SYS_LX_H +#define _SYS_LX_H + +#include <stdio.h> +#include <alloca.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/lwp.h> + +#include <sys/lx_brand.h> +#include <sys/lx_thread.h> + +#include <lx_errno.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern char lx_release[LX_KERN_RELEASE_MAX]; +extern char lx_cmd_name[MAXNAMLEN]; +extern pid_t zoneinit_pid; +extern int lx_is_vforked; +extern boolean_t lx_no_abort_handler; + +/* + * Values Linux expects for init + */ +#define LX_INIT_PID 1 + +/* + * the maximum length of messages to be output with lx_msg(), lx_err(), + * lx_debug(), or lx_unsupported(). + */ +#define LX_MSG_MAXLEN (128 + MAXPATHLEN) + +/* + * Linux scheduler priority ranges. + */ +#define LX_SCHED_PRIORITY_MIN_OTHER 0 +#define LX_SCHED_PRIORITY_MAX_OTHER 0 +#define LX_SCHED_PRIORITY_MIN_RRFIFO 1 +#define LX_SCHED_PRIORITY_MAX_RRFIFO 99 + +/* + * Based on code from brand_misc.h, but use of that is incompatible with the + * lx brand. + * + * These macros invoke a brandsys subcommand, B_TRUSS_POINT, which makes it + * easy to debug with DTrace. + */ +#define B_TRUSS_POINT 6 + +#define B_TRACE_POINT_5(a0, a1, a2, a3, a4) \ + (void) syscall(SYS_brand, B_TRUSS_POINT, (a0), (a1), (a2), (a3), (a4)) + +#define B_TRACE_POINT_4(a0, a1, a2, a3) \ + B_TRACE_POINT_5((a0), (a1), (a2), (a3), 0) + +#define B_TRACE_POINT_3(a0, a1, a2) \ + B_TRACE_POINT_5((a0), (a1), (a2), 0, 0) + +#define B_TRACE_POINT_2(a0, a1) \ + B_TRACE_POINT_5((a0), (a1), 0, 0, 0) + +#define B_TRACE_POINT_1(a0) \ + B_TRACE_POINT_5((a0), 0, 0, 0, 0) + +#define B_TRACE_POINT_0() \ + B_TRACE_POINT_5(0, 0, 0, 0, 0) + +/* + * Macros to access register state within a ucontext_t: + */ +#define LX_REG(ucp, r) ((ucp)->uc_mcontext.gregs[(r)]) + +/* + * normally we never want to write to stderr or stdout because it's unsafe + * to make assumptions about the underlying file descriptors. to protect + * against writes to these file descriptors we go ahead and close them + * our brand process initalization code. but there are still occasions + * where we are willing to make assumptions about our file descriptors + * and write to them. at thes times we should use one lx_msg() or + * lx_msg_error() + */ +extern void lx_msg(char *, ...); +extern void lx_err(char *, ...); +extern void lx_err_fatal(char *, ...); +extern void lx_unsupported(char *, ...); + +struct ucontext; + +extern ucontext_t *lx_syscall_regs(void); +extern uintptr_t lx_find_brand_sp(void); +extern const ucontext_t *lx_find_brand_uc(void); + +extern void lx_jump_to_linux(ucontext_t *) __NORETURN; + +extern char *lx_fd_to_path(int fd, char *buf, int buf_size); +extern int lx_lpid_to_spair(pid_t, pid_t *, lwpid_t *); +extern int lx_lpid_to_spid(pid_t, pid_t *); + +extern void lx_ptrace_init(); +extern int lx_ptrace_wait(siginfo_t *); +extern void lx_ptrace_fork(void); +extern void lx_ptrace_stop_if_option(int, boolean_t, ulong_t msg, ucontext_t *); +extern void lx_ptrace_clone_begin(int, boolean_t, int); + +extern int lx_check_alloca(size_t); +#define SAFE_ALLOCA(sz) (lx_check_alloca(sz) ? alloca(sz) : NULL) + +extern void lx_init_tsd(lx_tsd_t *); +extern int lx_alloc_stack(void **, size_t *); +extern void lx_install_stack(void *, size_t, lx_tsd_t *); +extern void lx_free_stack(void); +extern void lx_free_other_stacks(void); +extern void lx_stack_prefork(void); +extern void lx_stack_postfork(void); + +extern void lx_block_all_signals(); +extern void lx_unblock_all_signals(); +extern int lx_all_signals_blocked(); + +/* + * NO_UUCOPY disables calls to the uucopy* system calls to help with + * debugging brand library accesses to linux application memory. + */ +#ifdef NO_UUCOPY + +int uucopy_unsafe(const void *src, void *dst, size_t n); +int uucopystr_unsafe(const void *src, void *dst, size_t n); + +#define uucopy(src, dst, n) uucopy_unsafe((src), (dst), (n)) +#define uucopystr(src, dst, n) uucopystr_unsafe((src), (dst), (n)) + +#endif /* NO_UUCOPY */ + +/* + * We use these Private libc interfaces to defer signals during critical + * sections. + */ +extern void _sigon(void); +extern void _sigoff(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h new file mode 100644 index 0000000000..f9b239150d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h @@ -0,0 +1,163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. + */ + +#ifndef _LX_MOUNT_H +#define _LX_MOUNT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rpc/rpc.h> +#include <nfs/nfs.h> + +extern int lx_nfs_mount(char *, char *, char *, int, char *); + +/* + * mount() is significantly different between Linux and Solaris. The main + * difference is between the set of flags. Some flags on Linux can be + * translated to a Solaris equivalent, some are converted to a + * filesystem-specific option, while others have no equivalent whatsoever. + */ +#define LX_MS_MGC_VAL 0xC0ED0000 +#define LX_MS_RDONLY 0x00000001 +#define LX_MS_NOSUID 0x00000002 +#define LX_MS_NODEV 0x00000004 +#define LX_MS_NOEXEC 0x00000008 +#define LX_MS_SYNCHRONOUS 0x00000010 +#define LX_MS_REMOUNT 0x00000020 +#define LX_MS_MANDLOCK 0x00000040 +#define LX_MS_NOATIME 0x00000400 +#define LX_MS_NODIRATIME 0x00000800 +#define LX_MS_BIND 0x00001000 +#define LX_MS_MOVE 0x00002000 +#define LX_MS_REC 0x00004000 +#define LX_MS_SILENT 0x00008000 +#define LX_MS_POSIXACL 0x00010000 +#define LX_MS_UNBINDABLE 0x00020000 +#define LX_MS_PRIVATE 0x00040000 +#define LX_MS_SLAVE 0x00080000 +#define LX_MS_SHARED 0x00100000 +#define LX_MS_RELATIME 0x00200000 +#define LX_MS_KERNMOUNT 0x00400000 +#define LX_MS_I_VERSION 0x00800000 +#define LX_MS_STRICTATIME 0x01000000 +#define LX_MS_LAZYTIME 0x02000000 + +/* internal flags - ignored if passed in */ +#define LX_MS_NOSEC 0x10000000 +#define LX_MS_BORN 0x20000000 +#define LX_MS_ACTIVE 0x40000000 +#define LX_MS_NOUSER 0x80000000 + +#define LX_MS_SUPPORTED (LX_MS_MGC_VAL | \ + LX_MS_RDONLY | LX_MS_NOSUID | \ + LX_MS_NODEV | LX_MS_NOEXEC | \ + LX_MS_REMOUNT | LX_MS_NOATIME | \ + LX_MS_NODIRATIME | LX_MS_BIND | LX_MS_SILENT | \ + LX_MS_STRICTATIME | LX_MS_NOSEC | \ + LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER) + +/* + * support for nfs mounts + */ +#define LX_NMD_MAXHOSTNAMELEN 256 + +#define LX_NFS_MOUNT_SOFT 0x00000001 +#define LX_NFS_MOUNT_INTR 0x00000002 +#define LX_NFS_MOUNT_SECURE 0x00000004 +#define LX_NFS_MOUNT_POSIX 0x00000008 +#define LX_NFS_MOUNT_NOCTO 0x00000010 +#define LX_NFS_MOUNT_NOAC 0x00000020 +#define LX_NFS_MOUNT_TCP 0x00000040 +#define LX_NFS_MOUNT_VER3 0x00000080 +#define LX_NFS_MOUNT_KERBEROS 0x00000100 +#define LX_NFS_MOUNT_NONLM 0x00000200 +#define LX_NFS_MOUNT_BROKEN_SUID 0x00000400 +#define LX_NFS_MOUNT_SUPPORTED (LX_NFS_MOUNT_SOFT | \ + LX_NFS_MOUNT_INTR | \ + LX_NFS_MOUNT_POSIX | \ + LX_NFS_MOUNT_NOCTO | \ + LX_NFS_MOUNT_NOAC | \ + LX_NFS_MOUNT_TCP | \ + LX_NFS_MOUNT_VER3 | \ + LX_NFS_MOUNT_NONLM) + +#define LX_NMD_DEFAULT_RSIZE 0 +#define LX_NMD_DEFAULT_WSIZE 0 + +/* + * the nfs v3 file handle structure definitions are _almost_ the same + * on linux and solaris. the key difference are: + * + * 1) on linux fh3_length is an unsigned short where as on solaris it's + * an int. + * + * 2) on linux the file handle data doesn't 32 bit members, so the structure + * is not 32 bit aligned. (where as on solaris it is.) + * + * so rather than defining a structure that would allow us to intrepret + * all the contents of the nfs v3 file handle here, we decide to treate + * the file handle as an array of chars. this works just fine since it + * avoids the alignment issues and the actual file handle handle contects + * are defined by the nfs specification so they are common across solaris + * and linux. we do the same thing for nfs v2 file handles. + */ +struct lx_nfs_fh2 { + unsigned char lx_fh_data[NFS_FHSIZE]; +} lx_nfs_fh2; + +struct lx_nfs_fh3 { + unsigned short lx_fh3_length; + unsigned char lx_fh3_data[NFS3_FHSIZE]; +} lx_nfs_fh3; + +typedef struct lx_nfs_mount_data { + int nmd_version; + int nmd_fd; + struct lx_nfs_fh2 nmd_old_root; + int nmd_flags; + int nmd_rsize; + int nmd_wsize; + int nmd_timeo; + int nmd_retrans; + int nmd_acregmin; + int nmd_acregmax; + int nmd_acdirmin; + int nmd_acdirmax; + struct sockaddr_in nmd_addr; + char nmd_hostname[LX_NMD_MAXHOSTNAMELEN]; + int nmd_namlen; + uint_t nmd_bsize; + struct lx_nfs_fh3 nmd_root; +} lx_nfs_mount_data_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_MOUNT_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h new file mode 100644 index 0000000000..99abdbbf46 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_LX_POLL_H +#define _SYS_LX_POLL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * These events are identical between Linux and Solaris + */ +#define LX_POLLIN 0x001 +#define LX_POLLPRI 0x002 +#define LX_POLLOUT 0x004 +#define LX_POLLERR 0x008 +#define LX_POLLHUP 0x010 +#define LX_POLLNVAL 0x020 +#define LX_POLLRDNORM 0x040 +#define LX_POLLRDBAND 0x080 + +#define LX_POLL_COMMON_EVENTS (LX_POLLIN | LX_POLLPRI | LX_POLLOUT | \ + LX_POLLERR | LX_POLLHUP | LX_POLLNVAL | LX_POLLRDNORM | LX_POLLRDBAND) + +/* + * These events differ between Linux and Solaris + */ +#define LX_POLLWRNORM 0x0100 +#define LX_POLLWRBAND 0x0200 +#define LX_POLLRDHUP 0x2000 + + +#define LX_POLL_SUPPORTED_EVENTS \ + (LX_POLL_COMMON_EVENTS | LX_POLLWRNORM | LX_POLLWRBAND | LX_POLLRDHUP) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_POLL_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h new file mode 100644 index 0000000000..9d77524410 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h @@ -0,0 +1,289 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _SYS_LX_SIGNAL_H +#define _SYS_LX_SIGNAL_H + +#if !defined(_ASM) +#include <sys/lx_types.h> +#include <sys/ucontext.h> +#include <sys/lx_siginfo.h> +#include <lx_signum.h> + +#endif /* !defined(_ASM) */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Linux sigaction flags + */ +#define LX_SA_NOCLDSTOP 0x00000001 +#define LX_SA_NOCLDWAIT 0x00000002 +#define LX_SA_SIGINFO 0x00000004 +#define LX_SA_RESTORER 0x04000000 +#define LX_SA_ONSTACK 0x08000000 +#define LX_SA_RESTART 0x10000000 +#define LX_SA_NODEFER 0x40000000 +#define LX_SA_RESETHAND 0x80000000 +#define LX_SA_NOMASK LX_SA_NODEFER +#define LX_SA_ONESHOT LX_SA_RESETHAND + +#define LX_SIG_BLOCK 0 +#define LX_SIG_UNBLOCK 1 +#define LX_SIG_SETMASK 2 + +#define LX_MINSIGSTKSZ 2048 +#define LX_SS_ONSTACK 1 +#define LX_SS_DISABLE 2 + +#define LX_SIGRT_MAGIC 0xdeadf00d + +#if !defined(_ASM) + +typedef struct lx_sigaction { + void (*lxsa_handler)(); + int lxsa_flags; + void (*lxsa_restorer)(void); + lx_sigset_t lxsa_mask; +} lx_sigaction_t; + +#if defined(_ILP32) +typedef uint32_t lx_osigset_t; + +#define OSIGSET_NBITS (sizeof (lx_osigset_t) * NBBY) +#define OSIGSET_BITSET(sig) (1U << (((sig) - 1) % OSIGSET_NBITS)) + +typedef struct lx_osigaction { + void (*lxsa_handler)(); + lx_osigset_t lxsa_mask; + int lxsa_flags; + void (*lxsa_restorer)(void); +} lx_osigaction_t; +#endif + +/* + * Flag settings to determine whether common routines should operate on + * lx_sigset_ts or lx_osigset_ts. + */ +#define USE_OSIGSET 0 +#define USE_SIGSET 1 + +typedef struct lx_sighandlers { + struct lx_sigaction lx_sa[LX_NSIG + 1]; +} lx_sighandlers_t; + +typedef struct lx_sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +} lx_stack_t; + +/* + * _fpreg, _fpxreg, _xmmreg and _fpstate are defined in Linux src in: + * arch/x86/include/uapi/asm/sigcontext.h + */ +#define LX_X86_FXSR_MAGIC 0x0000 +#define LX_X86_FXSR_NONE 0xffff + +#if defined(_LP64) + +typedef struct lx_fpstate { + ushort_t cwd; + ushort_t swd; + ushort_t twd; /* Note this is not the same as the 32bit/x87/FSAVE twd */ + ushort_t fop; + uint64_t rip; + uint64_t rdp; + uint32_t mxcsr; + uint32_t mxcsr_mask; + uint32_t st_space[32]; /* 8 * 16 bytes for each FP-reg */ + uint32_t xmm_space[64]; /* 16 * 16 bytes for each XMM-reg */ + uint32_t reserved2[12]; + uint32_t reserved3[12]; +} lx_fpstate_t; + +/* + * The Linux layout is defined in the Linux src tree in: + * arch/x86/include/asm/sigcontext.h + * and the user-level def (which is what we want) at: + * arch/x86/include/uapi/asm/sigcontext.h + * + * The Illumos offsets of the registers in the context are defined in: + * usr/src/uts/intel/sys/regset.h + * this is an mcontext_t from uc_mcontext. + * + * For the 64-bit case the register layout is completely different in the + * context. + */ +typedef struct lx_sigcontext { + ulong_t sc_r8; + ulong_t sc_r9; + ulong_t sc_r10; + ulong_t sc_r11; + ulong_t sc_r12; + ulong_t sc_r13; + ulong_t sc_r14; + ulong_t sc_r15; + ulong_t sc_rdi; + ulong_t sc_rsi; + ulong_t sc_rbp; + ulong_t sc_rbx; + ulong_t sc_rdx; + ulong_t sc_rax; + ulong_t sc_rcx; + ulong_t sc_rsp; + ulong_t sc_rip; + ulong_t sc_eflags; + ushort_t sc_cs; + ushort_t sc_gs; + ushort_t sc_fs; + ushort_t sc_pad0; + ulong_t sc_err; + ulong_t sc_trapno; + + ulong_t sc_mask; + ulong_t sc_cr2; + lx_fpstate_t *sc_fpstate; + + ulong_t reserved[8]; +} lx_sigcontext_t; + +#else /* is _ILP32 */ + +struct lx_fpreg { + ushort_t significand[4]; + ushort_t exponent; +}; + +struct lx_fpxreg { + ushort_t significand[4]; + ushort_t exponent; + ushort_t padding[3]; +}; + +struct lx_xmmreg { + uint32_t element[4]; +}; + +typedef struct lx_fpstate { + /* Regular FPU environment */ + ulong_t cw; + ulong_t sw; + ulong_t tag; + ulong_t ipoff; + ulong_t cssel; + ulong_t dataoff; + ulong_t datasel; + struct lx_fpreg _st[8]; + ushort_t status; + ushort_t magic; /* 0xffff = regular FPU data */ + + /* FXSR FPU environment */ + ulong_t _fxsr_env[6]; /* env is ignored */ + ulong_t mxcsr; + ulong_t reserved; + struct lx_fpxreg _fxsr_st[8]; /* reg data is ignored */ + struct lx_xmmreg _xmm[8]; + ulong_t padding[56]; +} lx_fpstate_t; + +/* + * The Linux layout is defined in the Linux src tree in: + * arch/x86/include/asm/sigcontext.h + * and the user-level def (which is what we want) at: + * arch/x86/include/uapi/asm/sigcontext.h + * + * The Illumos offsets of the registers in the context are defined by the + * i386 ABI (see usr/src/uts/intel/sys/regset.h). + * + * Both Illumos and Linux match up here. + */ +typedef struct lx_sigcontext { + ulong_t sc_gs; + ulong_t sc_fs; + ulong_t sc_es; + ulong_t sc_ds; + ulong_t sc_edi; + ulong_t sc_esi; + ulong_t sc_ebp; + ulong_t sc_esp; + ulong_t sc_ebx; + ulong_t sc_edx; + ulong_t sc_ecx; + ulong_t sc_eax; + ulong_t sc_trapno; + ulong_t sc_err; + ulong_t sc_eip; + ulong_t sc_cs; + ulong_t sc_eflags; + ulong_t sc_esp_at_signal; + ulong_t sc_ss; + + lx_fpstate_t *sc_fpstate; + ulong_t sc_mask; + ulong_t sc_cr2; +} lx_sigcontext_t; +#endif + +typedef struct lx_ucontext { + ulong_t uc_flags; /* Linux always sets this to 0 */ + struct lx_ucontext *uc_link; /* Linux always sets this to NULL */ + lx_stack_t uc_stack; + lx_sigcontext_t uc_sigcontext; + lx_sigset_t uc_sigmask; +} lx_ucontext_t; + +typedef struct lx_sigbackup lx_sigbackup_t; +struct lx_sigbackup { + ucontext_t *lxsb_retucp; + ucontext_t *lxsb_sigucp; + uintptr_t lxsb_sigdeliver_frame; + lx_sigbackup_t *lxsb_previous; +}; + +extern const int ltos_signo[]; +extern const int stol_signo[]; + +extern void setsigacthandler(void (*)(int, siginfo_t *, void *), + void (**)(int, siginfo_t *, void *), + int (*)(const ucontext_t *)); + +extern int lx_siginit(void); +extern void lx_sighandlers_save(lx_sighandlers_t *); +extern void lx_sighandlers_restore(lx_sighandlers_t *); + +extern int stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop); +extern int stol_status(int); + +#endif /* !defined(_ASM) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_SIGNAL_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h new file mode 100644 index 0000000000..fbbf462389 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h @@ -0,0 +1,78 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _SYS_LX_SIGSTACK_H +#define _SYS_LX_SIGSTACK_H + +#if !defined(_ASM) +#include <sys/lx_types.h> +#include <sys/ucontext.h> +#include <sys/lx_signal.h> +#include <lx_signum.h> + +#endif /* !defined(_ASM) */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Two flavors of Linux signal stacks: + * + * lx_sigstack - used for "modern" signal handlers, in practice those + * that have the sigaction(2) flag SA_SIGINFO set + * + * lx_oldsigstack - used for legacy signal handlers, those that do not have + * the sigaction(2) flag SA_SIGINFO set or that were setup via + * the signal(2) call. + * + * NOTE: Since these structures will be placed on the stack and stack math will + * be done with their sizes, for the 32-bit code they must be word + * aligned in size (4 bytes) so the stack remains word aligned per the + * i386 ABI, or, for 64-bit code they must be 16 byte aligned as per the + * AMD64 ABI. + * + * The precise layout of these stack frames is also potentially + * depended on by particularly esoteric (or broken) software, and + * should be preserved. The Linux structures (rt_sigframe, et al) + * are defined in "arch/x86/include/asm/sigframe.h". + */ +#if defined(_LP64) +typedef struct lx_sigstack { + void (*retaddr)(); /* address of real lx_rt_sigreturn code */ + lx_ucontext_t uc; /* saved user context */ + lx_siginfo_t si; /* saved signal information */ + lx_fpstate_t fpstate; /* saved FP state */ + char pad[2]; /* stack alignment */ +} lx_sigstack_t; +#else +struct lx_sigstack { + void (*retaddr)(); /* address of real lx_rt_sigreturn code */ + int sig; /* signal number */ + lx_siginfo_t *sip; /* points to "si" if valid, NULL if not */ + lx_ucontext_t *ucp; /* points to "uc" */ + lx_siginfo_t si; /* saved signal information */ + lx_ucontext_t uc; /* saved user context */ + lx_fpstate_t fpstate; /* saved FP state */ + char trampoline[8]; /* code for trampoline to lx_rt_sigreturn() */ +}; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_SIGSTACK_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h new file mode 100644 index 0000000000..2bbc31a234 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h @@ -0,0 +1,147 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +#ifndef _SYS_LX_SOCKET_H +#define _SYS_LX_SOCKET_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/lx_types.h> + +/* + * Linux address family definitions + * Some of these are not supported + */ +#define LX_AF_UNSPEC 0 /* Unspecified */ +#define LX_AF_UNIX 1 /* local file/pipe name */ +#define LX_AF_INET 2 /* IP protocol family */ +#define LX_AF_AX25 3 /* Amateur Radio AX.25 */ +#define LX_AF_IPX 4 /* Novell Internet Protocol */ +#define LX_AF_APPLETALK 5 /* Appletalk */ +#define LX_AF_NETROM 6 /* Amateur radio */ +#define LX_AF_BRIDGE 7 /* Multiprotocol bridge */ +#define LX_AF_ATMPVC 8 /* ATM PVCs */ +#define LX_AF_X25 9 /* X.25 */ +#define LX_AF_INET6 10 /* IPV 6 */ +#define LX_AF_ROSE 11 /* Amateur Radio X.25 */ +#define LX_AF_DECnet 12 /* DECnet */ +#define LX_AF_NETBEUI 13 /* 802.2LLC */ +#define LX_AF_SECURITY 14 /* Security callback */ +#define LX_AF_KEY 15 /* key management */ +#define LX_AF_ROUTE 16 /* Alias to emulate 4.4BSD */ +#define LX_AF_PACKET 17 /* Packet family */ +#define LX_AF_ASH 18 /* Ash ? */ +#define LX_AF_ECONET 19 /* Acorn Econet */ +#define LX_AF_ATMSVC 20 /* ATM SVCs */ +#define LX_AF_SNA 22 /* Linux SNA */ +#define LX_AF_IRDA 23 /* IRDA sockets */ +#define LX_AF_PPPOX 24 /* PPPoX sockets */ +#define LX_AF_WANPIPE 25 /* Wanpipe API sockets */ +#define LX_AF_BLUETOOTH 31 /* Bluetooth sockets */ +#define LX_AF_MAX 33 /* MAX socket type */ + +#define AF_NOTSUPPORTED -1 +#define AF_INVAL -2 + +/* + * Linux ARP protocol hardware identifiers + */ +#define LX_ARPHRD_ETHER 1 /* Ethernet */ +#define LX_ARPHRD_LOOPBACK 772 /* Loopback */ +#define LX_ARPHRD_VOID 0xffff /* Unknown */ + +/* + * Linux socket type definitions + */ +#define LX_SOCK_STREAM 1 /* Connection-based byte streams */ +#define LX_SOCK_DGRAM 2 /* Connectionless, datagram */ +#define LX_SOCK_RAW 3 /* Raw protocol interface */ +#define LX_SOCK_RDM 4 /* Reliably-delivered message */ +#define LX_SOCK_SEQPACKET 5 /* Sequenced packet stream */ +#define LX_SOCK_PACKET 10 /* Linux specific */ +#define LX_SOCK_MAX 11 + +/* + * The Linux socket type can be or-ed with other flags (e.g. SOCK_CLOEXEC). + */ +#define LX_SOCK_TYPE_MASK 0xf + +/* + * Linux flags for socket, socketpair and accept4. These are or-ed into the + * socket type value. In the Linux net.h header these come from fcntl.h (note + * that they are in octal in the Linux header). + */ +#define LX_SOCK_CLOEXEC 0x80000 +#define LX_SOCK_NONBLOCK 0x800 + +#define SOCK_NOTSUPPORTED -1 +#define SOCK_INVAL -2 + +/* + * PF_PACKET protocol definitions. + */ +#define LX_ETH_P_802_3 0x0001 +#define LX_ETH_P_ALL 0x0003 +#define LX_ETH_P_802_2 0x0004 +#define LX_ETH_P_IP 0x0800 +#define LX_ETH_P_ARP 0x0806 +#define LX_ETH_P_IPV6 0x86DD + +/* + * Linux socketcall indices. + * These constitute all 17 socket related system calls + * + * These system calls are called via a single system call socketcall(). + * The first arg being the endex of the system call type + */ +#define LX_SOCKET 1 +#define LX_BIND 2 +#define LX_CONNECT 3 +#define LX_LISTEN 4 +#define LX_ACCEPT 5 +#define LX_GETSOCKNAME 6 +#define LX_GETPEERNAME 7 +#define LX_SOCKETPAIR 8 +#define LX_SEND 9 +#define LX_RECV 10 +#define LX_SENDTO 11 +#define LX_RECVFROM 12 +#define LX_SHUTDOWN 13 +#define LX_SETSOCKOPT 14 +#define LX_GETSOCKOPT 15 +#define LX_SENDMSG 16 +#define LX_RECVMSG 17 +#define LX_ACCEPT4 18 +#define LX_RECVMMSG 19 +#define LX_SENDMMSG 20 + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_SOCKET_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h new file mode 100644 index 0000000000..02f1a9b32b --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h @@ -0,0 +1,85 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. All rights reserved. + */ + +#ifndef _LX_STATFS_H +#define _LX_STATFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int lx_statfs_init(void); + +struct lx_statfs { + size_t f_type; + size_t f_bsize; + ulong_t f_blocks; + ulong_t f_bfree; + ulong_t f_bavail; + ulong_t f_files; + ulong_t f_ffree; + u_longlong_t f_fsid; + size_t f_namelen; + size_t f_frsize; + size_t f_spare[5]; +}; + +struct lx_statfs64 { + int f_type; + int f_bsize; + u_longlong_t f_blocks; + u_longlong_t f_bfree; + u_longlong_t f_bavail; + u_longlong_t f_files; + u_longlong_t f_ffree; + u_longlong_t f_fsid; + int f_namelen; + int f_frsize; + int f_spare[5]; +}; + +/* + * These magic values are taken mostly from statfs(2) or magic.h + */ +#define LX_AUTOFS_SUPER_MAGIC 0x0187 +#define LX_CGROUP_SUPER_MAGIC 0x27e0eb +#define LX_DEVFS_SUPER_MAGIC 0x1373 +#define LX_DEVPTS_SUPER_MAGIC 0x1cd1 +#define LX_EXT2_SUPER_MAGIC 0xEF53 +#define LX_ISOFS_SUPER_MAGIC 0x9660 +#define LX_MSDOS_SUPER_MAGIC 0x4d44 +#define LX_NFS_SUPER_MAGIC 0x6969 +#define LX_PROC_SUPER_MAGIC 0x9fa0 +#define LX_SYSFS_SUPER_MAGIC 0x62656572 +#define LX_TMPFS_SUPER_MAGIC 0x01021994 +#define LX_UFS_MAGIC 0x00011954 +#define LX_PIPEFS_MAGIC 0x50495045 + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_STATFS_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h new file mode 100644 index 0000000000..6b06140db2 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h @@ -0,0 +1,194 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +#ifndef _SYS_LX_SYSCALL_H +#define _SYS_LX_SYSCALL_H + +#include <sys/lx_brand.h> + +#if !defined(_ASM) + +#include <sys/types.h> +#include <sys/procset.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern int lx_install; + +extern long lx_mknodat(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_futimesat(uintptr_t, uintptr_t, uintptr_t); +extern long lx_utimensat(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_fstatat64(uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_stat(uintptr_t, uintptr_t); +extern long lx_fstat(uintptr_t, uintptr_t); +extern long lx_lstat(uintptr_t, uintptr_t); +extern long lx_stat64(uintptr_t, uintptr_t); +extern long lx_fstat64(uintptr_t, uintptr_t); +extern long lx_lstat64(uintptr_t, uintptr_t); +extern long lx_fcntl(uintptr_t, uintptr_t, uintptr_t); +extern long lx_fcntl64(uintptr_t, uintptr_t, uintptr_t); +extern long lx_readdir(uintptr_t, uintptr_t, uintptr_t); +extern long lx_execve(uintptr_t, uintptr_t, uintptr_t); +extern long lx_ioctl(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_settimeofday(uintptr_t, uintptr_t); +extern long lx_mknod(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_capget(uintptr_t, uintptr_t); +extern long lx_capset(uintptr_t, uintptr_t); + +extern long lx_clock_nanosleep(int, int flags, struct timespec *, + struct timespec *); +extern long lx_adjtimex(void *); +extern long lx_timer_settime(timer_t, int, struct itimerspec *, + struct itimerspec *); +extern long lx_timer_gettime(timer_t, struct itimerspec *); +extern long lx_timer_getoverrun(timer_t); +extern long lx_timer_delete(timer_t); + +extern long lx_truncate(uintptr_t, uintptr_t); +extern long lx_ftruncate(uintptr_t, uintptr_t); +extern long lx_truncate64(uintptr_t, uintptr_t, uintptr_t); +extern long lx_ftruncate64(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_sysctl(uintptr_t); +extern long lx_fsync(uintptr_t); +extern long lx_fdatasync(uintptr_t); +extern long lx_rmdir(uintptr_t); +extern long lx_utime(uintptr_t, uintptr_t); +extern long lx_sysfs(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_uname(uintptr_t); +extern long lx_getgroups16(uintptr_t, uintptr_t); +extern long lx_setgroups(uintptr_t, uintptr_t); +extern long lx_setgroups16(uintptr_t, uintptr_t); + +extern long lx_query_module(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + +extern long lx_setitimer(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_clone(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_exit(uintptr_t); +extern long lx_group_exit(uintptr_t); + +extern long lx_mount(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_statfs(uintptr_t, uintptr_t); +extern long lx_fstatfs(uintptr_t, uintptr_t); +extern long lx_statfs64(uintptr_t, uintptr_t, uintptr_t); +extern long lx_fstatfs64(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_sigreturn(void); +extern long lx_rt_sigreturn(void); +extern long lx_signal(uintptr_t, uintptr_t); +extern long lx_sigaction(uintptr_t, uintptr_t, uintptr_t); +extern long lx_rt_sigaction(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_sigaltstack(uintptr_t, uintptr_t); +extern long lx_sigpending(uintptr_t); +extern long lx_rt_sigpending(uintptr_t, uintptr_t); +extern long lx_sigprocmask(uintptr_t, uintptr_t, uintptr_t); +extern long lx_rt_sigprocmask(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_sigsuspend(uintptr_t); +extern long lx_rt_sigsuspend(uintptr_t, uintptr_t); +extern long lx_rt_sigwaitinfo(uintptr_t, uintptr_t, uintptr_t); +extern long lx_rt_sigtimedwait(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_rt_sigqueueinfo(uintptr_t, uintptr_t, uintptr_t); +extern long lx_rt_tgsigqueueinfo(uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_futex(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + +extern long lx_tkill(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); +extern long lx_tgkill(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + +extern long lx_sendfile(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_sendfile64(uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_fork(void); +extern long lx_vfork(void); +extern long lx_exec(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_ptrace(uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_xattr2(uintptr_t, uintptr_t); +extern long lx_xattr3(uintptr_t, uintptr_t, uintptr_t); +extern long lx_xattr4(uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_keyctl(void); + +extern long lx_ipc(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_msgget(key_t, int); +extern long lx_msgsnd(int, void *, size_t, int); +extern long lx_msgrcv(int, void *, size_t, long, int); +extern long lx_msgctl(int, int, void *); +extern long lx_semget(key_t, int, int); +extern long lx_semop(int, void *, size_t); +extern long lx_semtimedop(int, void *, size_t, struct timespec *); +extern long lx_semctl(int, int, int, void *); +extern long lx_shmget(key_t, size_t, int); +extern long lx_shmat(int, void *, int); +extern long lx_shmctl(int, int, void *); + +extern long lx_getgroups(int, gid_t *); +extern long lx_inotify_add_watch(int, const char *, uint32_t); +extern long lx_inotify_init(void); +extern long lx_inotify_init1(int); +extern long lx_inotify_rm_watch(int, int); +extern long lx_shmdt(char *); +extern long lx_signalfd(int, uintptr_t, size_t); +extern long lx_signalfd4(int, uintptr_t, size_t, int); +extern long lx_timerfd_create(int, int); +extern long lx_timerfd_settime(int, int, + const struct itimerspec *, struct itimerspec *); +extern long lx_timerfd_gettime(int, struct itimerspec *); +extern long lx_utimes(const char *, const struct timeval *); + +#endif /* !defined(_ASM) */ + + +#if defined(_LP64) +#define LX_SYS_clone 56 +#define LX_SYS_vfork 58 +#else +#define LX_SYS_clone 120 +#define LX_SYS_vfork 190 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_SYSCALL_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h new file mode 100644 index 0000000000..f9f49598b7 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h @@ -0,0 +1,222 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +#ifndef _LX_SYSV_IPC_H +#define _LX_SYSV_IPC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * msg-related definitions. + */ +#define LX_IPC_CREAT 00001000 +#define LX_IPC_EXCL 00002000 +#define LX_IPC_NOWAIT 00004000 + +#define LX_IPC_RMID 0 +#define LX_IPC_SET 1 +#define LX_IPC_STAT 2 +#define LX_IPC_INFO 3 + +#define LX_IPC_64 0x0100 + +#define LX_SEMOP 1 +#define LX_SEMGET 2 +#define LX_SEMCTL 3 +#define LX_MSGSND 11 +#define LX_MSGRCV 12 +#define LX_MSGGET 13 +#define LX_MSGCTL 14 +#define LX_SHMAT 21 +#define LX_SHMDT 22 +#define LX_SHMGET 23 +#define LX_SHMCTL 24 + +#define LX_MSG_STAT 11 +#define LX_MSG_INFO 12 + +#define LX_MSG_NOERROR 010000 + +/* + * Linux hard codes the maximum msgbuf length to be 8192 bytes. Really. + */ +#define LX_MSGMAX 8192 + +struct lx_ipc_perm { + key_t key; + uid_t uid; + uid_t gid; + uid_t cuid; + uid_t cgid; + ushort_t mode; + ushort_t _pad1; + ushort_t seq; + ushort_t _pad2; + ulong_t _unused1; + ulong_t _unused2; +}; + +struct lx_msqid_ds { + struct lx_ipc_perm msg_perm; + time_t msg_stime; +#if defined(_ILP32) + ulong_t _unused1; +#endif + time_t msg_rtime; +#if defined(_ILP32) + ulong_t _unused2; +#endif + time_t msg_ctime; +#if defined(_ILP32) + ulong_t _unused3; +#endif + ulong_t msg_cbytes; + ulong_t msg_qnum; + ulong_t msg_qbytes; + pid_t msg_lspid; + pid_t msg_lrpid; + ulong_t _unused4; + ulong_t _unused5; +}; + +struct lx_msginfo { + int msgpool; + int msgmap; + int msgmax; + int msgmnb; + int msgmni; + int msgssz; + int msgtql; + ushort_t msgseg; +}; + +/* + * semaphore-related definitions. + */ +#define LX_GETPID 11 +#define LX_GETVAL 12 +#define LX_GETALL 13 +#define LX_GETNCNT 14 +#define LX_GETZCNT 15 +#define LX_SETVAL 16 +#define LX_SETALL 17 +#define LX_SEM_STAT 18 +#define LX_SEM_INFO 19 +#define LX_SEM_UNDO 0x1000 +#define LX_SEMVMX 32767 + +struct lx_semid_ds { + struct lx_ipc_perm sem_perm; + time_t sem_otime; + ulong_t _unused1; + time_t sem_ctime; + ulong_t _unused2; + ulong_t sem_nsems; + ulong_t _unused3; + ulong_t _unused4; +}; + +struct lx_seminfo { + int semmap; + int semmni; + int semmns; + int semmnu; + int semmsl; + int semopm; + int semume; + int semusz; + int semvmx; + int semaem; +}; + +union lx_semun { + int val; + struct lx_semid_ds *semds; + ushort_t *sems; + struct lx_seminfo *info; + uintptr_t dummy; +}; + +/* + * shm-related definitions + */ +#define LX_SHM_LOCKED 02000 +#define LX_SHM_RDONLY 010000 +#define LX_SHM_RND 020000 +#define LX_SHM_REMAP 040000 + +#define LX_SHM_LOCK 11 +#define LX_SHM_UNLOCK 12 +#define LX_SHM_STAT 13 +#define LX_SHM_INFO 14 + +struct lx_shmid_ds { + struct lx_ipc_perm shm_perm; + size_t shm_segsz; + time_t shm_atime; +#if defined(_ILP32) + ulong_t _unused1; +#endif + time_t shm_dtime; +#if defined(_ILP32) + ulong_t _unused2; +#endif + time_t shm_ctime; +#if defined(_ILP32) + ulong_t _unused3; +#endif + pid_t shm_cpid; + pid_t shm_lpid; + ushort_t shm_nattch; + ulong_t _unused4; + ulong_t _unused5; +}; + +struct lx_shm_info { + int used_ids; + ulong_t shm_tot; + ulong_t shm_rss; + ulong_t shm_swp; + ulong_t swap_attempts; + ulong_t swap_successes; +}; + +struct lx_shminfo { + ulong_t shmmax; + ulong_t shmmin; + ulong_t shmmni; + ulong_t shmseg; + ulong_t shmall; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_SYSV_IPC_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h new file mode 100644 index 0000000000..0a17705dd1 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h @@ -0,0 +1,80 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc + */ + +#ifndef _SYS_LX_THREAD_H +#define _SYS_LX_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/lx_signal.h> +#include <thread.h> + +typedef enum lx_exit_type { + LX_ET_NONE = 0, + LX_ET_EXIT, + LX_ET_EXIT_GROUP +} lx_exit_type_t; + +typedef struct lx_tsd { + /* per-thread flag set on parent vfork, cleared on thread resume */ + int lxtsd_is_vforked; + lx_exit_type_t lxtsd_exit; + int lxtsd_exit_status; + ucontext_t lxtsd_exit_context; + + /* + * If this value is non-zero, we use it in lx_sigdeliver() to represent + * the in-use extent of the Linux (i.e. BRAND) stack for this thread. + * Access to this value must be protected by _sigoff()/_sigon(). + */ + uintptr_t lxtsd_lx_sp; + + /* + * Alternate stack for Linux sigaltstack emulation: + */ + lx_stack_t lxtsd_sigaltstack; + + void *lxtsd_clone_state; + + lx_sigbackup_t *lxtsd_sigbackup; +} lx_tsd_t; + +extern thread_key_t lx_tsd_key; + +extern void lx_swap_gs(long, long *); + +extern void lx_exit_common(void) __NORETURN; + +extern lx_tsd_t *lx_get_tsd(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_THREAD_H */ diff --git a/usr/src/lib/brand/lx/lx_init/Makefile b/usr/src/lib/brand/lx/lx_init/Makefile new file mode 100644 index 0000000000..7ce36ba51d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/Makefile @@ -0,0 +1,74 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2019 Joyent, Inc. +# + +PROG = lxinit + +PROG_OBJS = lxinit.o pipe_stream.o run_command.o +LIST_OBJS = list.o + +OBJS = $(PROG_OBJS) \ + $(LIST_OBJS) +SRCS = $(PROG_OBJS:%.o=%.c) \ + $(LIST_OBJS:%.o=$(SRC)/common/list/%.c) + +all: $(PROG) + +include ../Makefile.lx +include $(SRC)/cmd/Makefile.cmd + +# override the install directory +ROOTBIN = $(ROOTBRANDDIR) +CLOBBERFILES = $(OBJS) $(ROOTPROG) + +UTSBASE = $(SRC)/uts + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I$(UTSBASE)/common/brand/lx +LDLIBS += -lzonecfg -lipadm -lsocket -linetutil -lnsl -lcustr -ldhcpagent + +# not linted +SMATCH=off + +.KEEP_STATE: + +install: all $(ROOTPROG) + +clean: + $(RM) $(PROG) $(OBJS) + +lint: lint_PROG lint_SRCS + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.o: $(SRC)/common/list/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/lx/lx_init/lxinit.c b/usr/src/lib/brand/lx/lx_init/lxinit.c new file mode 100644 index 0000000000..983318c7eb --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/lxinit.c @@ -0,0 +1,934 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +/* + * lxinit performs zone-specific initialization prior to handing control to the + * guest Linux init. This primarily consists of: + * + * - Starting ipmgmtd + * - Configuring network interfaces + * - Adding a default route + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <limits.h> +#include <net/if.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stropts.h> +#include <sys/ioccom.h> +#include <sys/stat.h> +#include <sys/systeminfo.h> +#include <sys/sockio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/varargs.h> +#include <unistd.h> +#include <libintl.h> +#include <locale.h> +#include <libcustr.h> + +#include <netinet/dhcp.h> +#include <dhcpagent_util.h> +#include <dhcpagent_ipc.h> + +#include <arpa/inet.h> +#include <net/route.h> +#include <libipadm.h> +#include <libzonecfg.h> +#include <libinetutil.h> +#include <sys/lx_brand.h> + +#include "run_command.h" + +static void lxi_err(char *msg, ...) __NORETURN; +static void lxi_err(char *msg, ...); + +#define IPMGMTD_PATH "/lib/inet/ipmgmtd" +#define IN_NDPD_PATH "/usr/lib/inet/in.ndpd" +#define HOOK_POSTNET_PATH "/usr/lib/brand/lx/lx_hook_postnet" + +#define PREFIX_LOG_WARN "lx_init warn: " +#define PREFIX_LOG_ERR "lx_init err: " + +#define RTMBUFSZ (sizeof (struct rt_msghdr) + \ + (3 * sizeof (struct sockaddr_in))) + +ipadm_handle_t iph; + +static void +lxi_err(char *msg, ...) +{ + char buf[1024]; + int len; + va_list ap; + + va_start(ap, msg); + /*LINTED*/ + len = vsnprintf(buf, sizeof (buf), msg, ap); + va_end(ap); + + (void) write(1, PREFIX_LOG_ERR, strlen(PREFIX_LOG_ERR)); + (void) write(1, buf, len); + (void) write(1, "\n", 1); + + /* + * Since a non-zero exit will cause the zone to reboot, a pause here + * will prevent a mis-configured zone from spinning in a reboot loop. + */ + pause(); + exit(1); + /*NOTREACHED*/ +} + +static void +lxi_warn(char *msg, ...) +{ + char buf[1024]; + int len; + va_list ap; + + va_start(ap, msg); + /*LINTED*/ + len = vsnprintf(buf, sizeof (buf), msg, ap); + va_end(ap); + + (void) write(1, PREFIX_LOG_WARN, strlen(PREFIX_LOG_WARN)); + (void) write(1, buf, len); + (void) write(1, "\n", 1); +} + +static void +lxi_log_open() +{ + int fd = open("/dev/console", O_WRONLY); + + if (fd < 0) { + /* hard to log at this point... */ + exit(1); + } else if (fd != 1) { + /* + * Use stdout as the log fd. Init should start with no files + * open, so we should be required to perform this relocation + * every time. + */ + if (dup2(fd, 1) != 1) { + exit(1); + } + } +} + +static void +lxi_log_close() +{ + (void) close(0); + (void) close(1); +} + +static zone_dochandle_t +lxi_config_open() +{ + zoneid_t zoneid; + char zonename[ZONENAME_MAX]; + zone_dochandle_t handle; + zone_iptype_t iptype; + int res; + + zoneid = getzoneid(); + if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) < 0) { + lxi_err("could not determine zone name"); + } + + if ((handle = zonecfg_init_handle()) == NULL) + lxi_err("internal libzonecfg.so.1 error", 0); + + if ((res = zonecfg_get_handle(zonename, handle)) != Z_OK) { + zonecfg_fini_handle(handle); + lxi_err("could not locate zone config %d", res); + } + + /* + * Only exclusive stack is supported. + */ + if (zonecfg_get_iptype(handle, &iptype) != Z_OK || + iptype != ZS_EXCLUSIVE) { + zonecfg_fini_handle(handle); + lxi_err("lx zones do not support shared IP stacks"); + } + + return (handle); + +} + +static int +zone_find_attr(struct zone_res_attrtab *attrs, const char *name, + const char **result) +{ + while (attrs != NULL) { + if (strncmp(attrs->zone_res_attr_name, name, + MAXNAMELEN) == 0) { + *result = attrs->zone_res_attr_value; + return (0); + } + attrs = attrs->zone_res_attr_next; + } + return (-1); +} + +void +lxi_svc_start(char *name, char *path, char *fmri) +{ + pid_t pid; + int status; + char *const argv[] = { + name, + NULL + }; + char *const envp[] = { + fmri, + NULL + }; + + pid = fork(); + if (pid == -1) { + lxi_err("fork() failed: %s", strerror(errno)); + } + + if (pid == 0) { + /* child */ + const char *zroot = zone_get_nroot(); + char cmd[MAXPATHLEN]; + + /* + * Construct the full path to the binary, including the native + * system root (e.g. "/native") if in use for this zone: + */ + (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot != NULL ? + zroot : "", path); + + execve(cmd, argv, envp); + + lxi_err("execve(%s) failed: %s", cmd, strerror(errno)); + /* NOTREACHED */ + } + + /* parent */ + while (wait(&status) != pid) { + /* EMPTY */; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + lxi_err("%s[%d] exited: %d", name, + (int)pid, WEXITSTATUS(status)); + } + } else if (WIFSIGNALED(status)) { + lxi_err("%s[%d] died on signal: %d", name, + (int)pid, WTERMSIG(status)); + } else { + lxi_err("%s[%d] failed in unknown way", name, + (int)pid); + } +} + +void +lxi_net_ipmgmtd_start() +{ + lxi_svc_start("ipmgmtd", IPMGMTD_PATH, + "SMF_FMRI=svc:/network/ip-interface-management:default"); +} + +void +lxi_net_ndpd_start() +{ + lxi_svc_start("in.ndpd", IN_NDPD_PATH, + "SMF_FMRI=svc:/network/routing/ndp:default"); +} + + +static void +lxi_net_ipadm_open() +{ + ipadm_status_t status; + + if ((status = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) { + lxi_err("Error opening ipadm handle: %s", + ipadm_status2str(status)); + } +} + +static void +lxi_net_ipadm_close() +{ + ipadm_close(iph); +} + +void +lxi_net_plumb(const char *iface) +{ + ipadm_status_t status; + char ifbuf[LIFNAMSIZ]; + + /* ipadm_create_if stomps on ifbuf, so create a copy: */ + (void) strncpy(ifbuf, iface, sizeof (ifbuf)); + + if ((status = ipadm_create_if(iph, ifbuf, AF_INET, IPADM_OPT_ACTIVE)) + != IPADM_SUCCESS) { + lxi_err("ipadm_create_if error %d: %s/v4: %s", + status, iface, ipadm_status2str(status)); + } + + if ((status = ipadm_create_if(iph, ifbuf, AF_INET6, IPADM_OPT_ACTIVE)) + != IPADM_SUCCESS) { + lxi_err("ipadm_create_if error %d: %s/v6: %s", + status, iface, ipadm_status2str(status)); + } +} + +static int +lxi_getif(int af, char *iface, int len, boolean_t first_ipv4_configured) +{ + struct lifreq lifr; + int s = socket(af, SOCK_DGRAM, 0); + if (s < 0) { + lxi_warn("socket error %d: bringing up %s: %s", + errno, iface, strerror(errno)); + return (-1); + } + + /* + * We need a new logical interface for every IP address we add, except + * for the very first IPv4 address. + */ + if (af == AF_INET6 || first_ipv4_configured) { + (void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name)); + (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); + if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) { + if (close(s) != 0) { + lxi_warn("failed to close socket: %s\n", + strerror(errno)); + } + return (-1); + } + (void) strncpy(iface, lifr.lifr_name, len); + } + + if (close(s) != 0) { + lxi_warn("failed to close socket: %s\n", + strerror(errno)); + } + return (0); +} + +static int +lxi_iface_ip(const char *origiface, const char *addr, + boolean_t *first_ipv4_configured) +{ + static int addrnum = 0; + ipadm_status_t status; + ipadm_addrobj_t ipaddr = NULL; + char iface[LIFNAMSIZ]; + char aobjname[IPADM_AOBJSIZ]; + int af, err = 0; + + (void) strncpy(iface, origiface, sizeof (iface)); + + af = strstr(addr, ":") == NULL ? AF_INET : AF_INET6; + if (lxi_getif(af, iface, sizeof (iface), *first_ipv4_configured) != 0) { + lxi_warn("failed to create new logical interface " + "on %s: %s", origiface, strerror(errno)); + return (-1); + } + + (void) snprintf(aobjname, IPADM_AOBJSIZ, "%s/addr%d", iface, + addrnum++); + + if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname, + &ipaddr)) != IPADM_SUCCESS) { + lxi_warn("ipadm_create_addrobj error %d: addr %s, " + "interface %s: %s\n", status, addr, iface, + ipadm_status2str(status)); + return (-2); + } + + if ((status = ipadm_set_addr(ipaddr, addr, AF_UNSPEC)) + != IPADM_SUCCESS) { + lxi_warn("ipadm_set_addr error %d: addr %s" + ", interface %s: %s\n", status, addr, + iface, ipadm_status2str(status)); + err = -3; + goto done; + } + + if ((status = ipadm_create_addr(iph, ipaddr, + IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) { + lxi_warn("ipadm_create_addr error for %s: %s\n", iface, + ipadm_status2str(status)); + err = -4; + goto done; + } + + if (af == AF_INET) { + *first_ipv4_configured = B_TRUE; + } + +done: + ipadm_destroy_addrobj(ipaddr); + return (err); +} + +static int +lxi_iface_dhcp(const char *origiface, boolean_t *first_ipv4_configured) +{ + dhcp_ipc_request_t *dhcpreq = NULL; + dhcp_ipc_reply_t *dhcpreply = NULL; + int err = 0, timeout = 5; + char iface[LIFNAMSIZ]; + + (void) strncpy(iface, origiface, sizeof (iface)); + + if (lxi_getif(AF_INET, iface, sizeof (iface), *first_ipv4_configured) + != 0) { + lxi_warn("failed to create new logical interface " + "on %s: %s", origiface, strerror(errno)); + return (-1); + } + + if (dhcp_start_agent(timeout) != 0) { + lxi_err("Failed to start dhcpagent\n"); + /* NOTREACHED */ + } + + dhcpreq = dhcp_ipc_alloc_request(DHCP_START, iface, + NULL, 0, DHCP_TYPE_NONE); + if (dhcpreq == NULL) { + lxi_warn("Unable to allocate memory " + "to start DHCP on %s\n", iface); + return (-1); + } + + err = dhcp_ipc_make_request(dhcpreq, &dhcpreply, timeout); + if (err != 0) { + free(dhcpreq); + lxi_warn("Failed to start DHCP on %s: %s\n", iface, + dhcp_ipc_strerror(err)); + return (-1); + } + err = dhcpreply->return_code; + if (err != 0) { + lxi_warn("Failed to start DHCP on %s: %s\n", iface, + dhcp_ipc_strerror(err)); + goto done; + } + + *first_ipv4_configured = B_TRUE; + +done: + free(dhcpreq); + free(dhcpreply); + return (err); +} + +/* + * Initialize an IPv6 link-local address on a given interface + */ +static int +lxi_iface_ipv6_link_local(const char *iface) +{ + struct lifreq lifr; + int s; + + s = socket(AF_INET6, SOCK_DGRAM, 0); + if (s == -1) { + lxi_warn("socket error %d: bringing up %s: %s", + errno, iface, strerror(errno)); + } + + (void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name)); + if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { + lxi_warn("SIOCGLIFFLAGS error %d: bringing up %s: %s", + errno, iface, strerror(errno)); + return (-1); + } + + lifr.lifr_flags |= IFF_UP; + if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) { + lxi_warn("SIOCSLIFFLAGS error %d: bringing up %s: %s", + errno, iface, strerror(errno)); + return (-1); + } + + (void) close(s); + return (0); +} + +static int +lxi_iface_gateway(const char *iface, const char *dst, int dstpfx, + const char *gwaddr) +{ + int idx, len, sockfd; + char rtbuf[RTMBUFSZ]; + struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf; + struct sockaddr_in *dst_sin = (struct sockaddr_in *) + (rtbuf + sizeof (struct rt_msghdr)); + struct sockaddr_in *gw_sin = (struct sockaddr_in *)(dst_sin + 1); + struct sockaddr_in *netmask_sin = (struct sockaddr_in *)(gw_sin + 1); + + (void) bzero(rtm, RTMBUFSZ); + rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY; + rtm->rtm_msglen = sizeof (rtbuf); + rtm->rtm_pid = getpid(); + rtm->rtm_type = RTM_ADD; + rtm->rtm_version = RTM_VERSION; + + + /* + * The destination and netmask components have already been zeroed, + * which represents the default gateway. If we were passed a more + * specific destination network, use that instead. + */ + dst_sin->sin_family = AF_INET; + netmask_sin->sin_family = AF_INET; + if (dst != NULL) { + struct sockaddr *mask = (struct sockaddr *)netmask_sin; + + if ((inet_pton(AF_INET, dst, &(dst_sin->sin_addr))) != 1 || + plen2mask(dstpfx, AF_INET, mask) != 0) { + lxi_warn("bad destination network %s/%d: %s", dst, + dstpfx, strerror(errno)); + return (-1); + } + } + + if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) { + lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno)); + return (-1); + } + + if (iface != NULL) { + if ((idx = if_nametoindex(iface)) == 0) { + lxi_warn("unable to get interface index for %s: %s\n", + iface, strerror(errno)); + return (-1); + } + rtm->rtm_index = idx; + } + + if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { + lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno)); + return (-1); + } + + if ((len = write(sockfd, rtbuf, rtm->rtm_msglen)) < 0) { + lxi_warn("could not write rtmsg: %s", strerror(errno)); + close(sockfd); + return (-1); + } else if (len < rtm->rtm_msglen) { + lxi_warn("write() rtmsg incomplete"); + close(sockfd); + return (-1); + } + + close(sockfd); + return (0); +} + +static void +lxi_net_loopback() +{ + const char *iface = "lo0"; + boolean_t first_ipv4_configured = B_FALSE; + + lxi_net_plumb(iface); + (void) lxi_iface_ip(iface, "127.0.0.1/8", &first_ipv4_configured); + (void) lxi_iface_ipv6_link_local(iface); +} + + +/* + * This function is used when the "ips" property doesn't exist in a zone's + * configuration. It may be an older configuration, so we should search for + * "ip" and "netmask" and convert them into the new format. + */ +static int +lxi_get_old_ip(struct zone_res_attrtab *attrs, const char **ipaddrs, + char *cidraddr, int len) +{ + + const char *netmask; + int prefixlen; + struct sockaddr_in mask_sin; + + lxi_warn("Could not find \"ips\" property for zone. Looking " + "for older \"ip\" and \"netmask\" properties, instead."); + + if (zone_find_attr(attrs, "ip", ipaddrs) != 0) { + return (-1); + } + + if (strcmp(*ipaddrs, "dhcp") == 0) { + return (0); + } + + if (zone_find_attr(attrs, "netmask", &netmask) != 0) { + lxi_err("could not find netmask for interface"); + /* NOTREACHED */ + } + + /* Convert the netmask to a number */ + mask_sin.sin_family = AF_INET; + if (inet_pton(AF_INET, netmask, &mask_sin.sin_addr) != 1) { + lxi_err("invalid netmask address: %s\n", + strerror(errno)); + /* NOTREACHED */ + } + prefixlen = mask2plen((struct sockaddr *)&mask_sin); + + /* + * Write out the IP address in the new format and use + * that instead + */ + (void) snprintf(cidraddr, len, "%s/%d", *ipaddrs, prefixlen); + + *ipaddrs = cidraddr; + return (0); +} + +static void +lxi_net_setup(zone_dochandle_t handle) +{ + struct zone_nwiftab lookup; + boolean_t do_addrconf = B_FALSE; + + if (zonecfg_setnwifent(handle) != Z_OK) + return; + while (zonecfg_getnwifent(handle, &lookup) == Z_OK) { + const char *iface = lookup.zone_nwif_physical; + struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp; + const char *ipaddrs, *primary, *gateway; + char ipaddrs_copy[MAXNAMELEN], cidraddr[BUFSIZ], + *ipaddr, *tmp, *lasts; + boolean_t first_ipv4_configured = B_FALSE; + boolean_t *ficp = &first_ipv4_configured; + + lxi_net_plumb(iface); + if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 && + lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0) { + lxi_warn("Could not find a valid network configuration " + "for the %s interface", iface); + continue; + } + + if (lxi_iface_ipv6_link_local(iface) != 0) { + lxi_warn("unable to bring up link-local address on " + "interface %s", iface); + } + + /* + * If we're going to be doing DHCP, we have to do it first since + * dhcpagent doesn't like to operate on non-zero logical + * interfaces. + */ + if (strstr(ipaddrs, "dhcp") != NULL && + lxi_iface_dhcp(iface, ficp) != 0) { + lxi_warn("Failed to start DHCP on %s\n", iface); + } + + /* + * Copy the ipaddrs string, since strtok_r will write NUL + * characters into it. + */ + (void) strlcpy(ipaddrs_copy, ipaddrs, MAXNAMELEN); + tmp = ipaddrs_copy; + + /* + * Iterate over each IP and then set it up on the interface. + */ + while ((ipaddr = strtok_r(tmp, ",", &lasts)) != NULL) { + tmp = NULL; + if (strcmp(ipaddr, "addrconf") == 0) { + do_addrconf = B_TRUE; + } else if (strcmp(ipaddr, "dhcp") == 0) { + continue; + } else if (lxi_iface_ip(iface, ipaddr, ficp) < 0) { + lxi_warn("Unable to add new IP address (%s) " + "to interface %s", ipaddr, iface); + } + } + + if (zone_find_attr(attrs, "primary", &primary) == 0 && + strncmp(primary, "true", MAXNAMELEN) == 0 && + zone_find_attr(attrs, "gateway", &gateway) == 0) { + lxi_iface_gateway(iface, NULL, 0, gateway); + } + } + + if (do_addrconf) { + lxi_net_ndpd_start(); + } + + (void) zonecfg_endnwifent(handle); +} + +static void +lxi_net_static_route(const char *line) +{ + /* + * Each static route line is a string of the form: + * + * "10.77.77.2|10.1.1.0/24|false" + * + * i.e. gateway address, destination network, and whether this is + * a "link local" route or a next hop route. + */ + custr_t *cu = NULL; + char *gw = NULL, *dst = NULL; + int pfx = -1; + int i; + + if (custr_alloc(&cu) != 0) { + lxi_err("custr_alloc failure"); + } + + for (i = 0; line[i] != '\0'; i++) { + if (gw == NULL) { + if (line[i] == '|') { + if ((gw = strdup(custr_cstr(cu))) == NULL) { + lxi_err("strdup failure"); + } + custr_reset(cu); + } else { + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + continue; + } + + if (dst == NULL) { + if (line[i] == '/') { + if ((dst = strdup(custr_cstr(cu))) == NULL) { + lxi_err("strdup failure"); + } + custr_reset(cu); + } else { + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + continue; + } + + if (pfx == -1) { + if (line[i] == '|') { + pfx = atoi(custr_cstr(cu)); + custr_reset(cu); + } else { + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + continue; + } + + if (custr_appendc(cu, line[i]) != 0) { + lxi_err("custr_appendc failure"); + } + } + + /* + * We currently only support "next hop" routes, so ensure that + * "linklocal" is false: + */ + if (strcmp(custr_cstr(cu), "false") != 0) { + lxi_warn("invalid static route: %s", line); + } + + if (lxi_iface_gateway(NULL, dst, pfx, gw) != 0) { + lxi_err("failed to add route: %s/%d -> %s", dst, pfx, gw); + } + + custr_free(cu); + free(gw); + free(dst); +} + +static void +lxi_net_static_routes(void) +{ + const char *cmd = "/native/usr/lib/brand/lx/routeinfo"; + char *const argv[] = { "routeinfo", NULL }; + char *const envp[] = { NULL }; + int code; + struct stat st; + char errbuf[512]; + + if (stat(cmd, &st) != 0 || !S_ISREG(st.st_mode)) { + /* + * This binary is (potentially) shipped from another + * consolidation. If it does not exist, then the platform does + * not currently support static routes for LX-branded zones. + */ + return; + } + + /* + * Run the command, firing the callback for each line that it + * outputs. When this function returns, static route processing + * is complete. + */ + if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf), + lxi_net_static_route, &code) != 0 || code != 0) { + lxi_err("failed to run \"%s\": %s", cmd, errbuf); + } +} + +static void +lxi_config_close(zone_dochandle_t handle) +{ + zonecfg_fini_handle(handle); +} + +static void +lxi_hook_postnet() +{ + char cmd[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); + pid_t pid; + int status; + + (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot, HOOK_POSTNET_PATH); + if (access(cmd, X_OK) != 0) { + /* If no suitable script is present, soldier on. */ + return; + } + + if ((pid = fork()) < 0) { + lxi_err("fork() failed: %s", strerror(errno)); + } + if (pid == 0) { + char *const argv[] = { cmd, NULL }; + char *const envp[] = { NULL }; + + /* wire up stderr first, in case the hook wishes to use it */ + if (dup2(1, 2) < 0) { + lxi_err("dup2() failed: %s", cmd, strerror(errno)); + } + + /* child executes the hook */ + execve(cmd, argv, envp); + + /* + * Since this is running as root, access(2) is less strict than + * necessary to ensure a successful exec. If the permissions + * on the hook are busted, ignore the failure and move on. + */ + if (errno == EACCES) { + exit(0); + } + + lxi_err("execve(%s) failed: %s", cmd, strerror(errno)); + /* NOTREACHED */ + } + + /* Parent waits for the hook to complete */ + while (wait(&status) != pid) { + /* EMPTY */; + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + lxi_err("%s[%d] exited: %d", cmd, (int)pid, + WEXITSTATUS(status)); + } + } else if (WIFSIGNALED(status)) { + lxi_err("%s[%d] died on signal: %d", cmd, (int)pid, + WTERMSIG(status)); + } else { + lxi_err("%s[%d] failed in unknown way", cmd, (int)pid); + } +} + +static void +lxi_init_exec(char **argv) +{ + const char *cmd = "/sbin/init"; + char *const envp[] = { "container=zone", NULL }; + int e; + + argv[0] = "init"; + + /* + * systemd uses the 'container' env var to determine it is running + * inside a container. It only supports a few well-known types and + * treats anything else as 'other' but this is enough to make it + * behave better inside a zone. See 'detect_container' in systemd. + */ + execve(cmd, argv, envp); + e = errno; + + /* + * Because stdout was closed prior to exec, it must be opened again in + * the face of failure to log the error. + */ + lxi_log_open(); + lxi_err("execve(%s) failed: %s", cmd, strerror(e)); +} + +/*ARGSUSED*/ +int +main(int argc, char *argv[]) +{ + zone_dochandle_t handle; + + lxi_log_open(); + + lxi_net_ipmgmtd_start(); + lxi_net_ipadm_open(); + + handle = lxi_config_open(); + lxi_net_loopback(); + lxi_net_setup(handle); + lxi_config_close(handle); + + lxi_net_static_routes(); + + lxi_net_ipadm_close(); + + lxi_hook_postnet(); + + lxi_log_close(); + + lxi_init_exec(argv); + + /* NOTREACHED */ + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_init/pipe_stream.c b/usr/src/lib/brand/lx/lx_init/pipe_stream.c new file mode 100644 index 0000000000..8f06a07906 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/pipe_stream.c @@ -0,0 +1,326 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> +#include <unistd.h> +#include <port.h> +#include <poll.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/debug.h> +#include <sys/list.h> + +#include "pipe_stream.h" + +struct pipe_stream { + pipe_stream_loop_t *pis_loop; + + boolean_t pis_finished; + boolean_t pis_associated; + + void *pis_arg0; + void *pis_arg1; + + int pis_fd_write; + int pis_fd_read; + list_node_t pis_linkage; +}; + +struct pipe_stream_loop { + int psl_port; + + uint8_t *psl_buf; + size_t psl_buf_cap; + size_t psl_buf_occ; + + list_t psl_pipes; + + pipe_stream_data_cb *psl_cb_data; + pipe_stream_eof_cb *psl_cb_eof; + pipe_stream_error_cb *psl_cb_error; +}; + + +int +pipe_stream_loop_fini(pipe_stream_loop_t *psl) +{ + if (psl == NULL) { + return (0); + } + + VERIFY0(close(psl->psl_port)); + + while (!list_is_empty(&psl->psl_pipes)) { + pipe_stream_fini(list_head(&psl->psl_pipes)); + } + + list_destroy(&psl->psl_pipes); + free(psl); + + return (0); +} + +int +pipe_stream_loop_init(pipe_stream_loop_t **pslp, size_t bufsize, + pipe_stream_data_cb *data_cb, pipe_stream_eof_cb *eof_cb, + pipe_stream_error_cb *error_cb) +{ + pipe_stream_loop_t *psl; + + if ((psl = calloc(1, sizeof (*psl))) == NULL) { + return (-1); + } + + psl->psl_buf_cap = bufsize; + psl->psl_buf_occ = 0; + if ((psl->psl_buf = calloc(1, bufsize)) == NULL) { + free(psl); + return (-1); + } + + if ((psl->psl_port = port_create()) == -1) { + free(psl->psl_buf); + free(psl); + return (-1); + } + + psl->psl_cb_data = data_cb; + psl->psl_cb_eof = eof_cb; + psl->psl_cb_error = error_cb; + + list_create(&psl->psl_pipes, sizeof (pipe_stream_t), + offsetof(pipe_stream_t, pis_linkage)); + + *pslp = psl; + return (0); +} + +boolean_t +pipe_stream_loop_should_run(pipe_stream_loop_t *psl) +{ + pipe_stream_t *pis; + + for (pis = list_head(&psl->psl_pipes); pis != NULL; + pis = list_next(&psl->psl_pipes, pis)) { + if (!pis->pis_finished) { + return (B_TRUE); + } + } + + return (B_FALSE); +} + +int +pipe_stream_loop_run(pipe_stream_loop_t *psl) +{ + pipe_stream_t *pis; + port_event_t pev; + ssize_t sz; + + for (pis = list_head(&psl->psl_pipes); pis != NULL; + pis = list_next(&psl->psl_pipes, pis)) { + if (pis->pis_finished || pis->pis_associated) { + /* + * Skip streams that are already finished, as well as + * those that have already been associated with the + * port. + */ + continue; + } + + if (port_associate(psl->psl_port, PORT_SOURCE_FD, + pis->pis_fd_read, POLLIN, pis) != 0) { + return (-1); + } + } + +again: + if (port_get(psl->psl_port, &pev, NULL) != 0) { + switch (errno) { + case ETIME: + /* + * Timeout expired; return to caller. + */ + return (0); + + case EINTR: + /* + * Interrupted by signal. Try again. + */ + goto again; + + default: + return (-1); + } + } + + VERIFY(pev.portev_source == PORT_SOURCE_FD); + pis = (pipe_stream_t *)pev.portev_user; + VERIFY((int)pev.portev_object == pis->pis_fd_read); + pis->pis_associated = B_FALSE; + +read_again: + if ((sz = read(pis->pis_fd_read, psl->psl_buf, + psl->psl_buf_cap)) == -1) { + if (errno == EINTR) { + goto read_again; + } + + if (psl->psl_cb_error != NULL) { + psl->psl_cb_error(errno, pis->pis_arg0, pis->pis_arg1); + } + + VERIFY0(close(pis->pis_fd_read)); + pis->pis_fd_read = -1; + pis->pis_finished = B_TRUE; + } + psl->psl_buf_occ = sz; + + if (sz == 0) { + /* + * Stream EOF. + */ + pis->pis_finished = B_TRUE; + VERIFY0(close(pis->pis_fd_read)); + pis->pis_fd_read = -1; + if (psl->psl_cb_eof != NULL) { + psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1); + } + return (0); + } + + if (psl->psl_cb_data != NULL) { + int cbr = psl->psl_cb_data(psl->psl_buf, psl->psl_buf_occ, + pis->pis_arg0, pis->pis_arg1); + + if (cbr != 0) { + /* + * Callback failure: close file descriptor. + */ + pis->pis_finished = B_TRUE; + VERIFY0(close(pis->pis_fd_read)); + pis->pis_fd_read = -1; + if (psl->psl_cb_eof != NULL) { + psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1); + } + } + + return (0); + } + + return (0); +} + +int +pipe_stream_init(pipe_stream_loop_t *psl, pipe_stream_t **pisp, void *arg0, + void *arg1) +{ + int e = 0; + pipe_stream_t *pis; + int fds[2] = { -1, -1 }; + + if ((pis = calloc(1, sizeof (*pis))) == NULL) { + return (-1); + } + + if (pipe(fds) != 0) { + e = errno; + goto fail; + } + + pis->pis_fd_read = fds[0]; + pis->pis_fd_write = fds[1]; + + pis->pis_arg0 = arg0; + pis->pis_arg1 = arg1; + + pis->pis_finished = B_FALSE; + pis->pis_associated = B_FALSE; + + pis->pis_loop = psl; + list_insert_tail(&psl->psl_pipes, pis); + + *pisp = pis; + return (0); + +fail: + if (fds[0] != -1) { + VERIFY0(close(fds[0])); + } + if (fds[1] != -1) { + VERIFY0(close(fds[1])); + } + free(pis); + errno = e; + return (-1); +} + +int +pipe_stream_fini(pipe_stream_t *pis) +{ + if (pis == NULL) { + return (0); + } + + if (pis->pis_fd_read != -1) { + VERIFY0(close(pis->pis_fd_read)); + } + if (pis->pis_fd_write != -1) { + VERIFY0(close(pis->pis_fd_write)); + } + + list_remove(&pis->pis_loop->psl_pipes, pis); + + free(pis); + return (0); +} + +/* + * Called in the parent, after forking, to close the "write" end of the pipe. + */ +void +pipe_stream_parent_afterfork(pipe_stream_t *pis) +{ + if (pis->pis_fd_write != -1) { + (void) close(pis->pis_fd_write); + pis->pis_fd_write = -1; + } +} + +/* + * Called in the child, after forking, to close the "read" end of the + * pipe, and to dup the file descriptor into the right place. + */ +int +pipe_stream_child_afterfork(pipe_stream_t *pis, int dup_to) +{ + int e = 0; + + if (dup_to != -1) { + if (dup2(pis->pis_fd_write, dup_to) == -1) { + e = errno; + } + VERIFY0(close(pis->pis_fd_write)); + pis->pis_fd_write = dup_to; + } + + (void) close(pis->pis_fd_read); + pis->pis_fd_read = -1; + + errno = e; + return (e == 0 ? 0 : -1); +} diff --git a/usr/src/lib/brand/lx/lx_init/pipe_stream.h b/usr/src/lib/brand/lx/lx_init/pipe_stream.h new file mode 100644 index 0000000000..140ef18f8c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/pipe_stream.h @@ -0,0 +1,48 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _PIPE_STREAM_H +#define _PIPE_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int pipe_stream_data_cb(const uint8_t *, size_t, void *, void *); +typedef void pipe_stream_eof_cb(void *, void *); +typedef void pipe_stream_error_cb(int, void *, void *); + +typedef struct pipe_stream pipe_stream_t; +typedef struct pipe_stream_loop pipe_stream_loop_t; + +extern int pipe_stream_loop_fini(pipe_stream_loop_t *); +extern int pipe_stream_loop_init(pipe_stream_loop_t **, size_t, + pipe_stream_data_cb *, pipe_stream_eof_cb *, pipe_stream_error_cb *); + +extern int pipe_stream_init(pipe_stream_loop_t *, pipe_stream_t **, void *, + void *); +extern int pipe_stream_fini(pipe_stream_t *); + +extern void pipe_stream_parent_afterfork(pipe_stream_t *); +extern int pipe_stream_child_afterfork(pipe_stream_t *, int); + +extern boolean_t pipe_stream_loop_should_run(pipe_stream_loop_t *); +extern int pipe_stream_loop_run(pipe_stream_loop_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _PIPE_STREAM_H */ diff --git a/usr/src/lib/brand/lx/lx_init/run_command.c b/usr/src/lib/brand/lx/lx_init/run_command.c new file mode 100644 index 0000000000..9130838625 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/run_command.c @@ -0,0 +1,282 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <err.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <wait.h> +#include <sys/types.h> +#include <sys/debug.h> +#include <libcustr.h> + +#include "run_command.h" +#include "pipe_stream.h" + +typedef struct cmd { + int cmd_pid; + int cmd_wstatus; + pipe_stream_t *cmd_pipe[2]; + custr_t *cmd_err; + custr_t *cmd_out; + run_command_line_cb *cmd_func; + boolean_t cmd_cancel; +} cmd_t; + +static int cb_data(const uint8_t *, size_t, void *, void *); +static void cb_eof(void *, void *); +static void cb_error(int, void *, void *); + +void +post_error(cmd_t *cmd, const char *estr) +{ + if (cmd->cmd_cancel) { + return; + } + cmd->cmd_cancel = B_TRUE; + + custr_reset(cmd->cmd_err); + (void) custr_append(cmd->cmd_err, estr); +} + +int +run_command(const char *path, char *const argv[], char *const envp[], + char *errbuf, size_t errlen, run_command_line_cb *func, int *status) +{ + pipe_stream_loop_t *psl = NULL; + int e = 0; + cmd_t cmd; + pid_t wpid; + + bzero(&cmd, sizeof (cmd)); + + cmd.cmd_func = func; + + /* + * Allocate string buffers for stdout line buffering and error + * messages: + */ + if (custr_alloc_buf(&cmd.cmd_err, errbuf, errlen) != 0 || + custr_alloc(&cmd.cmd_out) != 0) { + e = errno; + goto out; + } + + /* + * Initialise pipe stream event loop: + */ + if (pipe_stream_loop_init(&psl, 256, cb_data, cb_eof, cb_error) != 0) { + e = errno; + post_error(&cmd, "could not init pipe stream loop"); + goto out; + } + + /* + * Create pipe streams for stdout and stderr communication with + * child process: + */ + if (pipe_stream_init(psl, &cmd.cmd_pipe[0], &cmd, + (void *)STDOUT_FILENO) != 0 || + pipe_stream_init(psl, &cmd.cmd_pipe[1], &cmd, + (void *)STDERR_FILENO) != 0) { + e = errno; + post_error(&cmd, "could not init pipe streams"); + goto out; + } + + /* + * Fork a child process: + */ + if ((cmd.cmd_pid = fork()) == -1) { + e = errno; + post_error(&cmd, "could not fork"); + goto out; + } + + if (cmd.cmd_pid == 0) { + /* + * This is the child process. Clean up file descriptors, and + * connect stdio to the pipes we allocated: + */ + VERIFY0(close(STDIN_FILENO)); + VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[0], + STDOUT_FILENO)); + VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[1], + STDERR_FILENO)); + closefrom(3); + + execve(path, argv, envp); + err(127, "exec(%s) failed", path); + } + + /* + * Back in the parent. Close the remote end of the stdio pipes: + */ + pipe_stream_parent_afterfork(cmd.cmd_pipe[0]); + pipe_stream_parent_afterfork(cmd.cmd_pipe[1]); + + /* + * Run the pipe event loop until all streams are completely + * consumed: + */ + while (pipe_stream_loop_should_run(psl)) { + if (pipe_stream_loop_run(psl) != 0) { + e = errno; + post_error(&cmd, "pipe stream loop run failure"); + goto out; + } + } + + /* + * Collect exit status of child process: + */ + while ((wpid = waitpid(cmd.cmd_pid, &cmd.cmd_wstatus, 0)) != + cmd.cmd_pid) { + if (wpid == -1 && errno != EINTR) { + e = errno; + post_error(&cmd, "waitpid failure"); + goto out; + } + } + + /* + * If the child died on a signal, fail the whole operation: + */ + if (WIFSIGNALED(cmd.cmd_wstatus)) { + e = ENXIO; + post_error(&cmd, "child process died on signal"); + (void) custr_append_printf(cmd.cmd_err, " (pid %d signal %d)", + cmd.cmd_pid, WTERMSIG(cmd.cmd_wstatus)); + goto out; + } + + /* + * If the child did not appear to exit, fail the whole operation: + */ + if (!WIFEXITED(cmd.cmd_wstatus)) { + e = ENXIO; + post_error(&cmd, "child process did not exit"); + (void) custr_append_printf(cmd.cmd_err, " (pid %d status %x)", + cmd.cmd_pid, cmd.cmd_wstatus); + goto out; + } + + /* + * Report exit status to the caller: + */ + *status = WEXITSTATUS(cmd.cmd_wstatus); + e = 0; + +out: + VERIFY0(pipe_stream_loop_fini(psl)); + /* + * Note that freeing the static error custr_t does not touch the + * underlying storage; we use this property to return the error + * message (if one exists) to the caller. + */ + custr_free(cmd.cmd_err); + custr_free(cmd.cmd_out); + errno = e; + return (e == 0 ? 0 : -1); +} + +static int +cb_data(const uint8_t *buf, size_t sz, void *arg0, void *arg1) +{ + cmd_t *cmd = arg0; + int fd = (int)arg1; + unsigned int i; + + if (cmd->cmd_cancel) { + return (-1); + } + + switch (fd) { + case STDOUT_FILENO: + for (i = 0; i < sz; i++) { + if (buf[i] == '\0' || buf[i] == '\r') { + continue; + } + + if (buf[i] == '\n') { + cmd->cmd_func(custr_cstr(cmd->cmd_out)); + custr_reset(cmd->cmd_out); + continue; + } + + if (custr_appendc(cmd->cmd_out, buf[i]) != 0) { + /* + * Failed to allocate memory; returning + * -1 here will abort the stream. + */ + post_error(cmd, "custr_appendc failure"); + return (-1); + } + } + break; + + case STDERR_FILENO: + /* + * Collect as much stderr output as will fit in our static + * buffer. + */ + for (i = 0; i < sz; i++) { + if (buf[i] == '\0') { + continue; + } + + (void) custr_appendc(cmd->cmd_err, buf[i]); + } + break; + + default: + abort(); + } + + return (0); +} + +static void +cb_eof(void *arg0, void *arg1) +{ + cmd_t *cmd = arg0; + int fd = (int)arg1; + + if (cmd->cmd_cancel) { + return; + } + + if (fd == STDOUT_FILENO && custr_len(cmd->cmd_out) > 0) { + cmd->cmd_func(custr_cstr(cmd->cmd_out)); + custr_reset(cmd->cmd_out); + } +} + +static void +cb_error(int e, void *arg0, void *arg1) +{ + cmd_t *cmd = arg0; + int fd = (int)arg1; + + if (cmd->cmd_cancel) { + return; + } + + post_error(cmd, "stream read failure"); + (void) custr_append_printf(cmd->cmd_err, " (pid %d fd %d): %s", + cmd->cmd_pid, fd, strerror(e)); +} diff --git a/usr/src/lib/brand/lx/lx_init/run_command.h b/usr/src/lib/brand/lx/lx_init/run_command.h new file mode 100644 index 0000000000..3484b265b6 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/run_command.h @@ -0,0 +1,32 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _RUN_COMMAND_H +#define _RUN_COMMAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void run_command_line_cb(const char *); + +extern int run_command(const char *, char *const [], char *const [], char *, + size_t, run_command_line_cb *, int *); + +#ifdef __cplusplus +} +#endif + +#endif /* _RUN_COMMAND_H */ diff --git a/usr/src/lib/brand/lx/lx_lockd/Makefile b/usr/src/lib/brand/lx/lx_lockd/Makefile new file mode 100644 index 0000000000..c1ff1b2d62 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_lockd/Makefile @@ -0,0 +1,65 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. + +# +# Copyright 2019 Joyent, Inc. +# + +PROG = lx_lockd + +PROG_OBJS = lockd.o nfs_tbind.o +UTIL_OBJS = thrpool.o + +OBJS = $(PROG_OBJS) $(UTIL_OBJS) +SRCS = $(PROG_OBJS:%.o=%.c) + +all: $(PROG) + +include ../Makefile.lx +include $(SRC)/cmd/Makefile.cmd + +# override the install directory +ROOTBIN = $(ROOTBRANDDIR) +CLOBBERFILES = $(OBJS) $(ROOTPROG) + +CPPFLAGS += -I$(SRC)/cmd/fs.d/nfs/lib +CSTD = $(CSTD_GNU99) +LDLIBS += -lnsl -lsocket + +CERRWARN += -_gcc=-Wno-parentheses +CERRWARN += -_gcc=-Wno-uninitialized +CERRWARN += -_gcc=-Wno-unused-function + +# not linted +SMATCH=off + +.KEEP_STATE: + +install: all $(ROOTPROG) + +clean: + $(RM) $(PROG) $(OBJS) + +lint: + $(LINT.c) -erroff=E_SEC_PRINTF_VAR_FMT lockd.c $(LDLIBS) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.o: $(SRC)/cmd/fs.d/nfs/lib/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/lx/lx_lockd/lockd.c b/usr/src/lib/brand/lx/lx_lockd/lockd.c new file mode 100644 index 0000000000..6a80b5355c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_lockd/lockd.c @@ -0,0 +1,568 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + + +/* + * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Joyent, Inc. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * University Copyright- Copyright (c) 1982, 1986, 1988 + * The Regents of the University of California + * All Rights Reserved + * + * University Acknowledgment- Portions of this document are derived from + * software developed by the University of California, Berkeley, and its + * contributors. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +/* + * lx-brand NFS lockd (NLM) server. + * + * This code is derived from the native lockd. The original history starts + * from: + * copied from usr/src/cmd/fs.d/nfs/nfsd/nfsd.c and then s:NFS:NLM: applied + * + * On Linux 'lockd' is implemented entirely inside the kernel, whereas our + * native lockd support is a combination of user-level and kernel code. + * Unfortunately, the native lockd is unable to run in lx for several reasons: + * - tightly bound to SMF + * - interacts with various native libnsl configuration files + * - expects to register with a native rpcbind using /dev/ticlts + * Thus, this code is derived from the native lockd, but modified to address + * these issues and run inside an lx-branded zone. Because the Linux lockd + * lives entirely in the kernel, our lockd must be started automatically if + * it is needed. This is done by the NFS mount code when it determines that + * a lockd is not already running. The kernel code ensures that there is only a + * single instance of the lockd running, in the case that there is a race with + * two NFS mounts occuring in parallel. + * + * lockd is required for both NFSv3 and v4 locking. Although v4 locking is + * part of the v4 protocol, the kernel support to allow NFS locking is enabled + * by the lockd when it starts. For v3, there must be a lockd registered with + * rpcbind or the server side will fail the lock. This is because the server + * side expects to make callbacks to the client. We must successfully register + * with the Linux rpcbind, otherwise the NFS syscall to enable the kernel side + * of locking will fail. For the v3 case, the user-level Linux mount helper cmd + * already checks for the presence of rpcbind. It fails if rpcbind is not + * running and the mount does not include the "nolock" option. For v4 the use + * of rpcbind appears unnecessary, since locking is built-in to the protocol, + * but it still required by our kernel NFS locking code. + * + * As with the native lockd, the kernel locking code makes upcalls to our lockd + * over /dev/ticotsord, so that device must be present inside an lx zone. + * + * Because this process tries to mimic the Linux kernel lockd, there is no + * stdin/out/err and we block all signals, unless we're running in debug mode. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <tiuser.h> +#include <rpc/rpc.h> +#include <errno.h> +#include <thread.h> +#include <sys/time.h> +#include <sys/file.h> +#include <nfs/nfs.h> +#include <nfs/nfssys.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <signal.h> +#include <netconfig.h> +#include <netdir.h> +#include <string.h> +#include <unistd.h> +#include <stropts.h> +#include <sys/tihdr.h> +#include <poll.h> +#include <priv_utils.h> +#include <sys/tiuser.h> +#include <netinet/tcp.h> +#include <deflt.h> +#include <rpcsvc/nlm_prot.h> +#include <ctype.h> +#include <strings.h> +#include <sys/varargs.h> +#include <rpcsvc/nlm_prot.h> +#include <rpc/pmap_prot.h> +#include "nfs_tbind.h" +#include "thrpool.h" + +/* Option defaults. See nfssys.h */ +struct lm_svc_args lmargs = { + .version = LM_SVC_CUR_VERS, + /* fd, n_fmly, n_proto, n_rdev (below) */ + .n_v4_only = 0, + .timout = 5 * 60, + .grace = 90, /* How long to wait for clients to re-establish locks. */ + .retransmittimeout = 5 +}; +int max_servers = 256; + +#define RET_OK 0 /* return code for no error */ +#define RET_ERR 33 /* return code for error(s) */ + +#define SYSLOG_BLEN 256 + +int nlmsvc(int fd, struct netbuf addrmask, struct netconfig *nconf); + +static int nlmsvcpool(int max_servers); +static void usage(void); + +static struct timeval tottimeout = { 60, 0 }; +static struct timeval rpcbtime = { 15, 0 }; +static const char nullstring[] = "\000"; + +boolean_t have_rpcbind = B_FALSE; + +extern int _nfssys(int, void *); +extern void nlm_do_one(char *, int (*)(int, struct netbuf, struct netconfig *)); +extern int nlm_bind_to_provider(char *, struct netbuf **, struct netconfig **); + +/* + * We want to bind to these TLI providers, and in this order, + * because the kernel NLM needs the loopback first for its + * initialization. (It uses it to talk to statd.) + */ +static NETSELDECL(defaultproviders)[] = { + "/dev/ticotsord", + "/dev/tcp", + "/dev/udp", + "/dev/tcp6", + "/dev/udp6", + NULL +}; + +/* + * The following are all globals used by routines in nfs_tbind.c. + */ +size_t end_listen_fds; /* used by conn_close_oldest() */ +size_t num_fds = 0; /* used by multiple routines */ +int listen_backlog = 32; /* used by bind_to_{provider,proto}() */ + /* used by cots_listen_event() */ +int max_conns_allowed = -1; /* used by cots_listen_event() */ +int (*Mysvc)(int, struct netbuf, struct netconfig *) = nlmsvc; + +boolean_t debug = B_FALSE; + +#define LX_PMAP_VERS 4 + +/* + * Set a mapping between program, version and address. + * Calls the lx-zone's rpcbind service to do the mapping. + */ +boolean_t +lx_rpcb_set(const rpcvers_t version, const struct netconfig *nconf, + const struct netbuf *address) +{ + CLIENT *client; + bool_t rslt = FALSE; + RPCB parms; + char uidbuf[32]; + + client = clnt_create_timed("localhost", PMAPPROG, LX_PMAP_VERS, + "datagram_v", &rpcbtime); + if (client == NULL) + return (B_FALSE); + + parms.r_addr = taddr2uaddr((struct netconfig *)nconf, + (struct netbuf *)address); /* convert to universal */ + if (!parms.r_addr) { + rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; + return (B_FALSE); + } + parms.r_prog = NLM_PROG; + parms.r_vers = version; + parms.r_netid = nconf->nc_netid; + /* + * Though uid is not being used directly, we still send it for + * completeness. For non-unix platforms, perhaps some other + * string or an empty string can be sent. + */ + (void) sprintf(uidbuf, "%d", (int)geteuid()); + parms.r_owner = uidbuf; + + CLNT_CALL(client, RPCBPROC_SET, (xdrproc_t)xdr_rpcb, (char *)&parms, + (xdrproc_t)xdr_bool, (char *)&rslt, tottimeout); + + CLNT_DESTROY(client); + free(parms.r_addr); + return (B_TRUE); +} + +/* + * Remove the mapping between program, version and netbuf address. + * Calls the rpcbind service to do the un-mapping. + */ +void +lx_rpcb_unset(const rpcvers_t version, char *nc_netid) +{ + CLIENT *client; + bool_t rslt; + RPCB parms; + char uidbuf[32]; + + if (!have_rpcbind) + return; + + client = clnt_create_timed("localhost", PMAPPROG, LX_PMAP_VERS, + "datagram_v", &rpcbtime); + if (client == NULL) + return; + + parms.r_prog = NLM_PROG; + parms.r_vers = version; + parms.r_netid = nc_netid; + parms.r_addr = (char *)&nullstring[0]; + (void) sprintf(uidbuf, "%d", (int)geteuid()); + parms.r_owner = uidbuf; + + CLNT_CALL(client, RPCBPROC_UNSET, (xdrproc_t)xdr_rpcb, (char *)&parms, + (xdrproc_t)xdr_bool, (char *)&rslt, tottimeout); + + CLNT_DESTROY(client); +} + +static void +lx_nlm_unreg(char *provider) +{ + struct netconfig *retnconf; + int vers; + + if (nlm_bind_to_provider(provider, NULL, &retnconf) == -1) + return; + + /* Unregister all versions of the program. */ + for (vers = NLM_VERS; vers <= NLM4_VERS; vers++) { + lx_rpcb_unset(vers, retnconf->nc_netid); + } +} + +void +lx_syslog(char *fmt, ...) +{ + int fd, l; + struct sockaddr_un snd_addr; + char buf[SYSLOG_BLEN], fb[SYSLOG_BLEN], *bp, *fp, *ep; + va_list ap; + + /* First we replace %m in fmt string with error string into fb. */ + ep = fb + sizeof (fb); + fb[SYSLOG_BLEN - 1] = '\0'; + for (bp = fb, fp = fmt; bp < ep && (*bp = *fp) != '\0'; bp++, fp++) { + if (*fp == '%' && *(fp + 1) == 'm') { + (void) strlcpy(bp, strerror(errno), ep - bp); + bp += strlen(bp); + fp += 2; + } + } + + va_start(ap, fmt); + (void) snprintf(buf, sizeof (buf), " rpc.lockd[%d]: ", getpid()); + l = strlen(buf); + bp = &buf[l]; + (void) vsnprintf(bp, sizeof (buf) - l, fb, ap); + va_end(ap); + + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) + return; + + bzero(&snd_addr, sizeof (snd_addr)); + strcpy(snd_addr.sun_path, "/dev/log"); + snd_addr.sun_family = AF_LOCAL; + l = strlen(snd_addr.sun_path) + sizeof (snd_addr.sun_family); + + if (connect(fd, (struct sockaddr *)&snd_addr, l) == 0) { + l = strlen(buf); + (void) send(fd, buf, l, 0); + } + + close(fd); +} + +/* When debugging, ensure cleanup */ +static void +sigint_handler(void) +{ + NETSELPDECL(providerp); + + lx_syslog("Stopping"); + + /* unregister from rpcbind */ + for (providerp = defaultproviders; *providerp != NULL; providerp++) { + char *provider = *providerp; + lx_nlm_unreg(provider); + } + (void) _nfssys(KILL_LOCKMGR, NULL); + + exit(0); +} + +int +main(int ac, char *av[]) +{ + NETSELPDECL(providerp); + int i, c, val; + + if (geteuid() != 0) { + exit(1); + } + + /* Initializations that require more privileges than we need to run. */ + if (__init_daemon_priv(PU_RESETGROUPS | PU_CLEARLIMITSET, 1, 1, + PRIV_SYS_NFS, NULL) == -1) { + exit(1); + } + + (void) enable_extended_FILE_stdio(-1, -1); + + while ((c = getopt(ac, av, "c:dg:l:t:")) != EOF) + switch (c) { + case 'c': /* max_connections */ + if ((val = atoi(optarg)) <= 0) + goto badval; + max_conns_allowed = val; + break; + + case 'd': /* debug */ + debug = B_TRUE; + break; + + case 'g': /* grace_period */ + if ((val = atoi(optarg)) <= 0) + goto badval; + lmargs.grace = val; + break; + + case 'l': /* listen_backlog */ + if ((val = atoi(optarg)) <= 0) + goto badval; + listen_backlog = val; + break; + + case 't': /* retrans_timeout */ + if ((val = atoi(optarg)) <= 0) + goto badval; + lmargs.retransmittimeout = val; + break; + + badval: + if (debug) { + fprintf(stderr, "Invalid -%c option value", c); + } + /* FALLTHROUGH */ + default: + if (debug) { + usage(); + } + exit(1); + } + + /* If there is one more argument, it is the number of servers. */ + if (optind < ac) { + val = atoi(av[optind]); + if (val <= 0) { + if (debug) { + fprintf(stderr, "Invalid max_servers argument"); + usage(); + } + exit(1); + } + max_servers = val; + optind++; + } + /* If there are two or more arguments, then this is a usage error. */ + if (optind != ac) { + if (debug) { + usage(); + } + exit(1); + } + + if (debug) { + printf("lx_lockd: debug=%d, conn_idle_timout=%d, " + "grace_period=%d, listen_backlog=%d, " + "max_connections=%d, max_servers=%d, " + "retrans_timeout=%d\n", + debug, lmargs.timout, lmargs.grace, listen_backlog, + max_conns_allowed, max_servers, lmargs.retransmittimeout); + } + + /* Set current dir to server root */ + if (chdir("/") < 0) { + lx_syslog("chdir: %m"); + exit(1); + } + + if (!debug) { + /* Block all signals if not debugging. */ + sigset_t set; + + (void) sigfillset(&set); + (void) sigprocmask(SIG_BLOCK, &set, NULL); + (void) setsid(); + } else { + struct sigaction act; + + act.sa_handler = sigint_handler; + act.sa_flags = 0; + (void) sigaction(SIGINT, &act, NULL); + } + + lx_syslog("Starting"); + + /* Unregister any previous versions. */ + for (i = NLM_VERS; i < NLM4_VERS; i++) { + svc_unreg(NLM_PROG, i); + } + + /* Set up kernel RPC thread pool for the NLM server. */ + if (nlmsvcpool(max_servers)) { + lx_syslog("Can't set up kernel NLM service: %m. Exiting"); + exit(1); + } + + /* Set up blocked thread to do LWP creation on behalf of the kernel. */ + if (svcwait(NLM_SVCPOOL_ID)) { + lx_syslog("Can't set up NLM pool creator: %m. Exiting"); + exit(1); + } + + for (providerp = defaultproviders; *providerp != NULL; providerp++) { + char *provider = *providerp; + nlm_do_one(provider, nlmsvc); + } + + if (num_fds == 0) { + lx_syslog("Could not start NLM service for any protocol. " + "Exiting"); + exit(1); + } + + end_listen_fds = num_fds; + + /* + * lockd is up and running as far as we are concerned. + * + * Get rid of unneeded privileges. + */ + __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, + PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); + + /* Poll for non-data control events on the transport descriptors. */ + poll_for_action(); + + /* If we get here, something failed in poll_for_action(). */ + return (1); +} + +static int +nlmsvcpool(int maxservers) +{ + struct svcpool_args npa; + + npa.id = NLM_SVCPOOL_ID; + npa.maxthreads = maxservers; + npa.redline = 0; + npa.qsize = 0; + npa.timeout = 0; + npa.stksize = 0; + npa.max_same_xprt = 0; + return (_nfssys(SVCPOOL_CREATE, &npa)); +} + +static int +ncfmly_to_lmfmly(const char *ncfmly) +{ + if (0 == strcmp(ncfmly, NC_INET)) + return (LM_INET); + if (0 == strcmp(ncfmly, NC_INET6)) + return (LM_INET6); + if (0 == strcmp(ncfmly, NC_LOOPBACK)) + return (LM_LOOPBACK); + return (-1); +} + +static int +nctype_to_lmprot(uint_t semantics) +{ + switch (semantics) { + case NC_TPI_CLTS: + return (LM_UDP); + case NC_TPI_COTS_ORD: + return (LM_TCP); + } + return (-1); +} + +static dev_t +ncdev_to_rdev(const char *ncdev) +{ + struct stat st; + + if (stat(ncdev, &st) < 0) + return (NODEV); + return (st.st_rdev); +} + +/* + * Establish NLM service thread. + */ +int +nlmsvc(int fd, struct netbuf addrmask, struct netconfig *nconf) +{ + struct lm_svc_args lma; + + lma = lmargs; /* init by struct copy */ + + /* + * The kernel code needs to reconstruct a complete + * knetconfig from n_fmly, n_proto. We use these + * two fields to convey the family and semantics. + */ + lma.fd = fd; + lma.n_fmly = ncfmly_to_lmfmly(nconf->nc_protofmly); + lma.n_proto = nctype_to_lmprot(nconf->nc_semantics); + lma.n_rdev = ncdev_to_rdev(nconf->nc_device); + + if (!have_rpcbind) { + /* + * Inform the kernel NLM code to run without rpcbind and + * rpc.statd. + */ + lma.n_v4_only = -1; + } + + return (_nfssys(LM_SVC, &lma)); +} + +static void +usage(void) +{ + (void) fprintf(stderr, "usage: lx_lockd [options] [max_servers]\n"); + (void) fprintf(stderr, "\t-c max_connections\n"); + (void) fprintf(stderr, "\t-d enable debugging\n"); + (void) fprintf(stderr, "\t-g grace_period\n"); + (void) fprintf(stderr, "\t-l listen_backlog\n"); + (void) fprintf(stderr, "\t-t retransmit_timeout\n"); +} diff --git a/usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c b/usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c new file mode 100644 index 0000000000..1f8f50e23c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c @@ -0,0 +1,1832 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Gary Mills + * Copyright 2017 Joyent, Inc. + */ + +/* + * Derived from usr/src/cmd/fs.d/nfs/lib/nfs_tbind.c, but modified to work + * within an lx-branded zone and to only support the lx lockd. Changes from the + * original have been minimized, thus this file is not cstyle or lint clean. + */ + +#include <tiuser.h> +#include <fcntl.h> +#include <netconfig.h> +#include <stropts.h> +#include <errno.h> +#include <rpc/rpc.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <signal.h> +#include <netdir.h> +#include <unistd.h> +#include <string.h> +#include <netinet/tcp.h> +#include <malloc.h> +#include <stdlib.h> +#include "nfs_tbind.h" +#include <nfs/nfs.h> +#include <nfs/nfs_acl.h> +#include <nfs/nfssys.h> +#include <nfs/nfs4.h> +#include <zone.h> +#include <sys/socket.h> +#include <tsol/label.h> +#include <sys/varargs.h> +#include <rpcsvc/nlm_prot.h> + +/* + * Determine valid semantics for most applications. + */ +#define OK_TPI_TYPE(_nconf) \ + (_nconf->nc_semantics == NC_TPI_CLTS || \ + _nconf->nc_semantics == NC_TPI_COTS || \ + _nconf->nc_semantics == NC_TPI_COTS_ORD) + +#define BE32_TO_U32(a) \ + ((((ulong_t)((uchar_t *)a)[0] & 0xFF) << (ulong_t)24) | \ + (((ulong_t)((uchar_t *)a)[1] & 0xFF) << (ulong_t)16) | \ + (((ulong_t)((uchar_t *)a)[2] & 0xFF) << (ulong_t)8) | \ + ((ulong_t)((uchar_t *)a)[3] & 0xFF)) + +/* + * Number of elements to add to the poll array on each allocation. + */ +#define POLL_ARRAY_INC_SIZE 64 + +/* + * Number of file descriptors by which the process soft limit may be + * increased on each call to nofile_increase(0). + */ +#define NOFILE_INC_SIZE 64 + +/* + * Default TCP send and receive buffer size of NFS server. + */ +#define NFSD_TCP_BUFSZ (1024*1024) + +struct conn_ind { + struct conn_ind *conn_next; + struct conn_ind *conn_prev; + struct t_call *conn_call; +}; + +struct conn_entry { + bool_t closing; + struct netconfig nc; +}; + +/* + * this file contains transport routines common to nfsd and lockd + */ +static int nofile_increase(int); +static int reuseaddr(int); +static int recvucred(int); +static int anonmlp(int); +static void add_to_poll_list(int, struct netconfig *); +static char *serv_name_to_port_name(char *); +static int bind_to_proto(char *, char *, struct netbuf **, + struct netconfig **); +int nlm_bind_to_provider(char *, struct netbuf **, struct netconfig **); +static void conn_close_oldest(void); +static boolean_t conn_get(int, struct netconfig *, struct conn_ind **); +static void cots_listen_event(int, int); +static int discon_get(int, struct netconfig *, struct conn_ind **); +static int do_poll_clts_action(int, int); +static int do_poll_cots_action(int, int); +static void remove_from_poll_list(int); +static int set_addrmask(int, struct netconfig *, struct netbuf *); +static int is_listen_fd_index(int); + +static struct pollfd *poll_array; +static struct conn_entry *conn_polled; +static int num_conns; /* Current number of connections */ +int (*Mysvc4)(int, struct netbuf *, struct netconfig *, int, + struct netbuf *); +static int setopt(int fd, int level, int name, int value); +static int get_opt(int fd, int level, int name); +static void nfslib_set_sockbuf(int fd); + +extern boolean_t have_rpcbind; +extern void lx_syslog(char *, ...); +extern boolean_t lx_rpcb_set(const rpcvers_t, const struct netconfig *, + const struct netbuf *); +extern void lx_rpcb_unset(const rpcvers_t, char *); + +/* We can't include syslog.h since we override it, so define these here. */ +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_DEBUG 7 + +/* + * Built-in netconfig table. + */ +#define N_NETCONF_ENTS 5 +static struct netconfig nca[N_NETCONF_ENTS] = { + {"udp6", NC_TPI_CLTS, 1, "inet6", "udp", "/dev/udp6", 0, NULL}, + {"tcp6", NC_TPI_COTS_ORD, 1, "inet6", "tcp", "/dev/tcp6", 0, NULL}, + {"udp", NC_TPI_CLTS, 1, "inet", "udp", "/dev/udp", 0, NULL}, + {"tcp", NC_TPI_COTS_ORD, 1, "inet", "tcp", "/dev/tcp", 0, NULL}, + {"ticotsord", NC_TPI_COTS_ORD, 1, "loopback", "-", "/dev/ticotsord", 0, + NULL} +}; + +/* To minimize file diffs we provide our own syslog wrapper. */ +static void +syslog(int level, char *fmt, ...) +{ + va_list ap; + + if (level == LOG_DEBUG) + return; + va_start(ap, fmt); + lx_syslog(fmt, ap); + va_end(ap); +} + +/* + * Called to create and prepare a transport descriptor for in-kernel + * RPC service. + * Returns -1 on failure and a valid descriptor on success. + */ +int +nfslib_transport_open(struct netconfig *nconf) +{ + int fd; + struct strioctl strioc; + + if ((nconf == (struct netconfig *)NULL) || + (nconf->nc_device == (char *)NULL)) { + syslog(LOG_ERR, "no netconfig device"); + return (-1); + } + + /* + * Open the transport device. + */ + fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL); + if (fd == -1) { + if (t_errno == TSYSERR && errno == EMFILE && + (nofile_increase(0) == 0)) { + /* Try again with a higher NOFILE limit. */ + fd = t_open(nconf->nc_device, O_RDWR, + (struct t_info *)NULL); + } + if (fd == -1) { + syslog(LOG_ERR, "t_open %s failed: t_errno %d, %m", + nconf->nc_device, t_errno); + return (-1); + } + } + + /* + * Pop timod because the RPC module must be as close as possible + * to the transport. + */ + if (ioctl(fd, I_POP, 0) < 0) { + syslog(LOG_ERR, "I_POP of timod failed: %m"); + (void) t_close(fd); + return (-1); + } + + /* + * Common code for CLTS and COTS transports + */ + if (ioctl(fd, I_PUSH, "rpcmod") < 0) { + syslog(LOG_ERR, "I_PUSH of rpcmod failed: %m"); + (void) t_close(fd); + return (-1); + } + + strioc.ic_cmd = RPC_SERVER; + strioc.ic_dp = (char *)0; + strioc.ic_len = 0; + strioc.ic_timout = -1; + + /* Tell rpcmod to act like a server stream. */ + if (ioctl(fd, I_STR, &strioc) < 0) { + syslog(LOG_ERR, "rpcmod set-up ioctl failed: %m"); + (void) t_close(fd); + return (-1); + } + + /* + * Re-push timod so that we will still be doing TLI + * operations on the descriptor. + */ + if (ioctl(fd, I_PUSH, "timod") < 0) { + syslog(LOG_ERR, "I_PUSH of timod failed: %m"); + (void) t_close(fd); + return (-1); + } + + /* + * Enable options of returning the ip's for udp. + */ + if (strcmp(nconf->nc_netid, "udp6") == 0) + __rpc_tli_set_options(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1); + else if (strcmp(nconf->nc_netid, "udp") == 0) + __rpc_tli_set_options(fd, IPPROTO_IP, IP_RECVDSTADDR, 1); + + return (fd); +} + +static int +nofile_increase(int limit) +{ + struct rlimit rl; + + if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { + syslog(LOG_ERR, "getrlimit of NOFILE failed: %m"); + return (-1); + } + + if (limit > 0) + rl.rlim_cur = limit; + else + rl.rlim_cur += NOFILE_INC_SIZE; + + if (rl.rlim_cur > rl.rlim_max && + rl.rlim_max != RLIM_INFINITY) + rl.rlim_max = rl.rlim_cur; + + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { + syslog(LOG_ERR, "setrlimit of NOFILE to %d failed: %m", + rl.rlim_cur); + return (-1); + } + + return (0); +} + +static void +nfslib_set_sockbuf(int fd) +{ + int curval, val; + + val = NFSD_TCP_BUFSZ; + + curval = get_opt(fd, SOL_SOCKET, SO_SNDBUF); + syslog(LOG_DEBUG, "Current SO_SNDBUF value is %d", curval); + if ((curval != -1) && (curval < val)) { + syslog(LOG_DEBUG, "Set SO_SNDBUF option to %d", val); + if (setopt(fd, SOL_SOCKET, SO_SNDBUF, val) < 0) { + syslog(LOG_ERR, + "couldn't set SO_SNDBUF to %d - t_errno = %d", + val, t_errno); + syslog(LOG_ERR, + "Check and increase system-wide tcp_max_buf"); + } + } + + curval = get_opt(fd, SOL_SOCKET, SO_RCVBUF); + syslog(LOG_DEBUG, "Current SO_RCVBUF value is %d", curval); + if ((curval != -1) && (curval < val)) { + syslog(LOG_DEBUG, "Set SO_RCVBUF option to %d", val); + if (setopt(fd, SOL_SOCKET, SO_RCVBUF, val) < 0) { + syslog(LOG_ERR, + "couldn't set SO_RCVBUF to %d - t_errno = %d", + val, t_errno); + syslog(LOG_ERR, + "Check and increase system-wide tcp_max_buf"); + } + } +} + +static int +nlm_bindit(struct netconfig *nconf, struct netbuf **addr, int backlog) +{ + int fd; + struct t_bind *ntb; + struct t_bind tb; + struct nd_addrlist *addrlist; + struct t_optmgmt req, resp; + struct opthdr *opt; + char reqbuf[128]; + struct nd_hostserv hs; + + if ((fd = nfslib_transport_open(nconf)) == -1) { + syslog(LOG_ERR, "cannot establish transport service over %s", + nconf->nc_device); + return (-1); + } + + addrlist = (struct nd_addrlist *)NULL; + + /* + * This is the well-defined 'lockd' port from /etc/services. We can + * pass down the NLM port number to nlm_bindit and that resolves + * properly in the native *getby* calling paths. + */ + hs.h_serv = "4045"; + hs.h_host = HOST_SELF; + + /* + * The following block is based on _netdir_getbyname in + * usr/src/lib/nametoaddr/straddr/common/straddr.c. This is what would + * be used by __classic_netdir_getbyname for the loopback. + */ + if (strcmp(nconf->nc_netid, "ticotsord") == 0) { + struct nd_addrlist *ap; + struct netbuf *netbufp; + char *fulladdr = "localhost.4045"; + + if ((ap = malloc(sizeof (struct nd_addrlist))) == NULL) { + (void) t_close(fd); + return (-1); + } + + ap->n_cnt = 1; + if ((ap->n_addrs = malloc(sizeof (struct netbuf))) == NULL) { + free(ap); + (void) t_close(fd); + return (-1); + } + + netbufp = ap->n_addrs; + + /* Don't include the terminating NULL character in the length */ + netbufp->len = netbufp->maxlen = (int)strlen(fulladdr); + if ((netbufp->buf = strdup(fulladdr)) == NULL) { + free(netbufp); + free(ap); + (void) t_close(fd); + return (-1); + } + addrlist = ap; + + } else if (netdir_getbyname(nconf, &hs, &addrlist) != 0) { + syslog(LOG_ERR, + "Cannot get address for transport %s", + nconf->nc_netid); + (void) t_close(fd); + return (-1); + } + + if (strcmp(nconf->nc_proto, "tcp") == 0) { + /* + * If we're running over TCP, then set the + * SO_REUSEADDR option so that we can bind + * to our preferred address even if previously + * left connections exist in FIN_WAIT states. + * This is somewhat bogus, but otherwise you have + * to wait 2 minutes to restart after killing it. + */ + if (reuseaddr(fd) == -1) { + syslog(LOG_WARNING, + "couldn't set SO_REUSEADDR option on transport"); + } + } else if (strcmp(nconf->nc_proto, "udp") == 0) { + /* + * In order to run MLP on UDP, we need to handle creds. + */ + if (recvucred(fd) == -1) { + syslog(LOG_WARNING, + "couldn't set SO_RECVUCRED option on transport"); + } + } + + if (nconf->nc_semantics == NC_TPI_CLTS) + tb.qlen = 0; + else + tb.qlen = backlog; + + /* LINTED pointer alignment */ + ntb = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL); + if (ntb == (struct t_bind *)NULL) { + syslog(LOG_ERR, "t_alloc failed: t_errno %d, %m", t_errno); + (void) t_close(fd); + netdir_free((void *)addrlist, ND_ADDRLIST); + return (-1); + } + + /* + * XXX - what about the space tb->addr.buf points to? This should + * be either a memcpy() to/from the buf fields, or t_alloc(fd,T_BIND,) + * should't be called with T_ALL. + */ + if (addrlist) + tb.addr = *(addrlist->n_addrs); /* structure copy */ + + if (t_bind(fd, &tb, ntb) == -1) { + syslog(LOG_ERR, "t_bind failed: t_errno %d, %m", t_errno); + (void) t_free((char *)ntb, T_BIND); + netdir_free((void *)addrlist, ND_ADDRLIST); + (void) t_close(fd); + return (-1); + } + + /* make sure we bound to the right address */ + if (tb.addr.len != ntb->addr.len || + memcmp(tb.addr.buf, ntb->addr.buf, tb.addr.len) != 0) { + syslog(LOG_ERR, "t_bind to wrong address"); + (void) t_free((char *)ntb, T_BIND); + netdir_free((void *)addrlist, ND_ADDRLIST); + (void) t_close(fd); + return (-1); + } + + *addr = &ntb->addr; + netdir_free((void *)addrlist, ND_ADDRLIST); + + if (strcmp(nconf->nc_proto, "tcp") == 0) { + /* + * Disable the Nagle algorithm on TCP connections. + * Connections accepted from this listener will + * inherit the listener options. + */ + + /* LINTED pointer alignment */ + opt = (struct opthdr *)reqbuf; + opt->level = IPPROTO_TCP; + opt->name = TCP_NODELAY; + opt->len = sizeof (int); + + /* LINTED pointer alignment */ + *(int *)((char *)opt + sizeof (*opt)) = 1; + + req.flags = T_NEGOTIATE; + req.opt.len = sizeof (*opt) + opt->len; + req.opt.buf = (char *)opt; + resp.flags = 0; + resp.opt.buf = reqbuf; + resp.opt.maxlen = sizeof (reqbuf); + + if (t_optmgmt(fd, &req, &resp) < 0 || + resp.flags != T_SUCCESS) { + syslog(LOG_ERR, + "couldn't set NODELAY option for proto %s: t_errno = %d, %m", + nconf->nc_proto, t_errno); + } + + nfslib_set_sockbuf(fd); + } + + return (fd); +} + +static int +get_opt(int fd, int level, int name) +{ + struct t_optmgmt req, res; + struct { + struct opthdr opt; + int value; + } reqbuf; + + reqbuf.opt.level = level; + reqbuf.opt.name = name; + reqbuf.opt.len = sizeof (int); + reqbuf.value = 0; + + req.flags = T_CURRENT; + req.opt.len = sizeof (reqbuf); + req.opt.buf = (char *)&reqbuf; + + res.flags = 0; + res.opt.buf = (char *)&reqbuf; + res.opt.maxlen = sizeof (reqbuf); + + if (t_optmgmt(fd, &req, &res) < 0 || res.flags != T_SUCCESS) { + t_error("t_optmgmt"); + return (-1); + } + return (reqbuf.value); +} + +static int +setopt(int fd, int level, int name, int value) +{ + struct t_optmgmt req, resp; + struct { + struct opthdr opt; + int value; + } reqbuf; + + reqbuf.opt.level = level; + reqbuf.opt.name = name; + reqbuf.opt.len = sizeof (int); + + reqbuf.value = value; + + req.flags = T_NEGOTIATE; + req.opt.len = sizeof (reqbuf); + req.opt.buf = (char *)&reqbuf; + + resp.flags = 0; + resp.opt.buf = (char *)&reqbuf; + resp.opt.maxlen = sizeof (reqbuf); + + if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) { + t_error("t_optmgmt"); + return (-1); + } + return (0); +} + +static int +reuseaddr(int fd) +{ + return (setopt(fd, SOL_SOCKET, SO_REUSEADDR, 1)); +} + +static int +recvucred(int fd) +{ + return (setopt(fd, SOL_SOCKET, SO_RECVUCRED, 1)); +} + +static int +anonmlp(int fd) +{ + return (setopt(fd, SOL_SOCKET, SO_ANON_MLP, 1)); +} + +void +nfslib_log_tli_error(char *tli_name, int fd, struct netconfig *nconf) +{ + int error; + + /* + * Save the error code across syslog(), just in case syslog() + * gets its own error and, therefore, overwrites errno. + */ + error = errno; + if (t_errno == TSYSERR) { + syslog(LOG_ERR, "%s(file descriptor %d/transport %s) %m", + tli_name, fd, nconf->nc_proto); + } else { + syslog(LOG_ERR, + "%s(file descriptor %d/transport %s) TLI error %d", + tli_name, fd, nconf->nc_proto, t_errno); + } + errno = error; +} + +/* + * Called to set up service over a particular transport. + */ +void +nlm_do_one(char *provider, int (*svc)(int, struct netbuf, struct netconfig *)) +{ + register int sock; + struct netbuf *retaddr; + struct netconfig *retnconf; + struct netbuf addrmask; + int vers; + int err; + static boolean_t elogged = B_FALSE; + + sock = nlm_bind_to_provider(provider, &retaddr, &retnconf); + if (sock == -1) { + (void) syslog(LOG_ERR, + "Cannot establish NLM service over %s: transport setup problem.", + provider); + return; + } + + if (set_addrmask(sock, retnconf, &addrmask) < 0) { + (void) syslog(LOG_ERR, + "Cannot set address mask for %s", retnconf->nc_netid); + return; + } + + /* Register all versions of the NLM with rpcbind. */ + if (strcmp(provider, "/dev/ticotsord") != 0) { + for (vers = NLM_VERS; vers <= NLM4_VERS; vers++) { + lx_rpcb_unset(vers, retnconf->nc_netid); + have_rpcbind = lx_rpcb_set(vers, retnconf, retaddr); + if (!have_rpcbind && !elogged) { + /* + * No rpcbind running. The kernel NFS locking + * code interacts with rpcbind & rpc.statd for + * NFSv3 locking. We warn about this but + * proceed since NFSv4 locking is still usable. + */ + lx_syslog("Warning: rpcbind is not running, " + "but is required for NFSv3 locking"); + elogged = B_TRUE; + break; + } + } + } + + /* + * Register services with CLTS semantics right now. + * Note: services with COTS/COTS_ORD semantics will be + * registered later from cots_listen_event function. + */ + if (retnconf->nc_semantics == NC_TPI_CLTS) { + /* Don't drop core if supporting module(s) aren't loaded. */ + (void) signal(SIGSYS, SIG_IGN); + + /* + * svc() doesn't block, it returns success or failure. + */ + + if (svc == NULL && Mysvc4 != NULL) + err = (*Mysvc4)(sock, &addrmask, retnconf, + NFS4_SETPORT|NFS4_KRPC_START, retaddr); + else + err = (*svc)(sock, addrmask, retnconf); + + if (err < 0) { + (void) syslog(LOG_ERR, + "Cannot establish NLM service over <file desc." + " %d, protocol %s> : %m. Exiting", + sock, retnconf->nc_proto); + exit(1); + } + } + free(addrmask.buf); + + /* + * We successfully set up the server over this transport. + * Add this descriptor to the one being polled on. + */ + add_to_poll_list(sock, retnconf); +} + +/* + * Set up the NFS service over all the available transports. + * Returns -1 for failure, 0 for success. + */ +int +do_all(struct protob *protobp, + int (*svc)(int, struct netbuf, struct netconfig *)) +{ + struct netconfig *nconf; + NCONF_HANDLE *nc; + int l; + + if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) { + syslog(LOG_ERR, "setnetconfig failed: %m"); + return (-1); + } + l = strlen(NC_UDP); + while (nconf = getnetconfig(nc)) { + if ((nconf->nc_flag & NC_VISIBLE) && + strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0 && + OK_TPI_TYPE(nconf) && + (protobp->program != NFS4_CALLBACK || + strncasecmp(nconf->nc_proto, NC_UDP, l) != 0)) + continue; /* do_all should never be called */ + } + (void) endnetconfig(nc); + return (0); +} + +/* + * poll on the open transport descriptors for events and errors. + */ +void +poll_for_action(void) +{ + int nfds; + int i; + + /* + * Keep polling until all transports have been closed. When this + * happens, we return. + */ + while ((int)num_fds > 0) { + nfds = poll(poll_array, num_fds, INFTIM); + switch (nfds) { + case 0: + continue; + + case -1: + /* + * Some errors from poll could be + * due to temporary conditions, and we try to + * be robust in the face of them. Other + * errors (should never happen in theory) + * are fatal (eg. EINVAL, EFAULT). + */ + switch (errno) { + case EINTR: + continue; + + case EAGAIN: + case ENOMEM: + (void) sleep(10); + continue; + + default: + (void) syslog(LOG_ERR, + "poll failed: %m. Exiting"); + exit(1); + } + default: + break; + } + + /* + * Go through the poll list looking for events. + */ + for (i = 0; i < num_fds && nfds > 0; i++) { + if (poll_array[i].revents) { + nfds--; + /* + * We have a message, so try to read it. + * Record the error return in errno, + * so that syslog(LOG_ERR, "...%m") + * dumps the corresponding error string. + */ + if (conn_polled[i].nc.nc_semantics == + NC_TPI_CLTS) { + errno = do_poll_clts_action( + poll_array[i].fd, i); + } else { + errno = do_poll_cots_action( + poll_array[i].fd, i); + } + + if (errno == 0) + continue; + /* + * Most returned error codes mean that there is + * fatal condition which we can only deal with + * by closing the transport. + */ + if (errno != EAGAIN && errno != ENOMEM) { + (void) syslog(LOG_ERR, + "Error (%m) reading descriptor %d/transport %s. Closing it.", + poll_array[i].fd, + conn_polled[i].nc.nc_proto); + (void) t_close(poll_array[i].fd); + remove_from_poll_list(poll_array[i].fd); + + } else if (errno == ENOMEM) + (void) sleep(5); + } + } + } + + (void) syslog(LOG_ERR, + "All transports have been closed with errors. Exiting."); +} + +/* + * Allocate poll/transport array entries for this descriptor. + */ +static void +add_to_poll_list(int fd, struct netconfig *nconf) +{ + static int poll_array_size = 0; + + /* + * If the arrays are full, allocate new ones. + */ + if (num_fds == poll_array_size) { + struct pollfd *tpa; + struct conn_entry *tnp; + + if (poll_array_size != 0) { + tpa = poll_array; + tnp = conn_polled; + } else + tpa = (struct pollfd *)0; + + poll_array_size += POLL_ARRAY_INC_SIZE; + /* + * Allocate new arrays. + */ + poll_array = (struct pollfd *) + malloc(poll_array_size * sizeof (struct pollfd) + 256); + conn_polled = (struct conn_entry *) + malloc(poll_array_size * sizeof (struct conn_entry) + 256); + if (poll_array == (struct pollfd *)NULL || + conn_polled == (struct conn_entry *)NULL) { + syslog(LOG_ERR, "malloc failed for poll array"); + exit(1); + } + + /* + * Copy the data of the old ones into new arrays, and + * free the old ones. + */ + if (tpa) { + (void) memcpy((void *)poll_array, (void *)tpa, + num_fds * sizeof (struct pollfd)); + (void) memcpy((void *)conn_polled, (void *)tnp, + num_fds * sizeof (struct conn_entry)); + free((void *)tpa); + free((void *)tnp); + } + } + + /* + * Set the descriptor and event list. All possible events are + * polled for. + */ + poll_array[num_fds].fd = fd; + poll_array[num_fds].events = POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI; + + /* + * Copy the transport data over too. + */ + conn_polled[num_fds].nc = *nconf; + conn_polled[num_fds].closing = 0; + + /* + * Set the descriptor to non-blocking. Avoids a race + * between data arriving on the stream and then having it + * flushed before we can read it. + */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + (void) syslog(LOG_ERR, + "fcntl(file desc. %d/transport %s, F_SETFL, O_NONBLOCK): %m. Exiting", + num_fds, nconf->nc_proto); + exit(1); + } + + /* + * Count this descriptor. + */ + ++num_fds; +} + +static void +remove_from_poll_list(int fd) +{ + int i; + int num_to_copy; + + for (i = 0; i < num_fds; i++) { + if (poll_array[i].fd == fd) { + --num_fds; + num_to_copy = num_fds - i; + (void) memcpy((void *)&poll_array[i], + (void *)&poll_array[i+1], + num_to_copy * sizeof (struct pollfd)); + (void) memset((void *)&poll_array[num_fds], 0, + sizeof (struct pollfd)); + (void) memcpy((void *)&conn_polled[i], + (void *)&conn_polled[i+1], + num_to_copy * sizeof (struct conn_entry)); + (void) memset((void *)&conn_polled[num_fds], 0, + sizeof (struct conn_entry)); + return; + } + } + syslog(LOG_ERR, "attempt to remove nonexistent fd from poll list"); + +} + +/* + * Called to read and interpret the event on a connectionless descriptor. + * Returns 0 if successful, or a UNIX error code if failure. + */ +static int +do_poll_clts_action(int fd, int conn_index) +{ + int error; + int ret; + int flags; + struct netconfig *nconf = &conn_polled[conn_index].nc; + static struct t_unitdata *unitdata = NULL; + static struct t_uderr *uderr = NULL; + static int oldfd = -1; + struct nd_hostservlist *host = NULL; + struct strbuf ctl[1], data[1]; + /* + * We just need to have some space to consume the + * message in the event we can't use the TLI interface to do the + * job. + * + * We flush the message using getmsg(). For the control part + * we allocate enough for any TPI header plus 32 bytes for address + * and options. For the data part, there is nothing magic about + * the size of the array, but 256 bytes is probably better than + * 1 byte, and we don't expect any data portion anyway. + * + * If the array sizes are too small, we handle this because getmsg() + * (called to consume the message) will return MOREDATA|MORECTL. + * Thus we just call getmsg() until it's read the message. + */ + char ctlbuf[sizeof (union T_primitives) + 32]; + char databuf[256]; + + /* + * If this is the same descriptor as the last time + * do_poll_clts_action was called, we can save some + * de-allocation and allocation. + */ + if (oldfd != fd) { + oldfd = fd; + + if (unitdata) { + (void) t_free((char *)unitdata, T_UNITDATA); + unitdata = NULL; + } + if (uderr) { + (void) t_free((char *)uderr, T_UDERROR); + uderr = NULL; + } + } + + /* + * Allocate a unitdata structure for receiving the event. + */ + if (unitdata == NULL) { + /* LINTED pointer alignment */ + unitdata = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL); + if (unitdata == NULL) { + if (t_errno == TSYSERR) { + /* + * Save the error code across + * syslog(), just in case + * syslog() gets its own error + * and therefore overwrites errno. + */ + error = errno; + (void) syslog(LOG_ERR, + "t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed: %m", + fd, nconf->nc_proto); + return (error); + } + (void) syslog(LOG_ERR, +"t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed TLI error %d", + fd, nconf->nc_proto, t_errno); + goto flush_it; + } + } + +try_again: + flags = 0; + + /* + * The idea is we wait for T_UNITDATA_IND's. Of course, + * we don't get any, because rpcmod filters them out. + * However, we need to call t_rcvudata() to let TLI + * tell us we have a T_UDERROR_IND. + * + * algorithm is: + * t_rcvudata(), expecting TLOOK. + * t_look(), expecting T_UDERR. + * t_rcvuderr(), expecting success (0). + * expand destination address into ASCII, + * and dump it. + */ + + ret = t_rcvudata(fd, unitdata, &flags); + if (ret == 0 || t_errno == TBUFOVFLW) { + (void) syslog(LOG_WARNING, +"t_rcvudata(file descriptor %d/transport %s) got unexpected data, %d bytes", + fd, nconf->nc_proto, unitdata->udata.len); + + /* + * Even though we don't expect any data, in case we do, + * keep reading until there is no more. + */ + if (flags & T_MORE) + goto try_again; + + return (0); + } + + switch (t_errno) { + case TNODATA: + return (0); + case TSYSERR: + /* + * System errors are returned to caller. + * Save the error code across + * syslog(), just in case + * syslog() gets its own error + * and therefore overwrites errno. + */ + error = errno; + (void) syslog(LOG_ERR, + "t_rcvudata(file descriptor %d/transport %s) %m", + fd, nconf->nc_proto); + return (error); + case TLOOK: + break; + default: + (void) syslog(LOG_ERR, + "t_rcvudata(file descriptor %d/transport %s) TLI error %d", + fd, nconf->nc_proto, t_errno); + goto flush_it; + } + + ret = t_look(fd); + switch (ret) { + case 0: + return (0); + case -1: + /* + * System errors are returned to caller. + */ + if (t_errno == TSYSERR) { + /* + * Save the error code across + * syslog(), just in case + * syslog() gets its own error + * and therefore overwrites errno. + */ + error = errno; + (void) syslog(LOG_ERR, + "t_look(file descriptor %d/transport %s) %m", + fd, nconf->nc_proto); + return (error); + } + (void) syslog(LOG_ERR, + "t_look(file descriptor %d/transport %s) TLI error %d", + fd, nconf->nc_proto, t_errno); + goto flush_it; + case T_UDERR: + break; + default: + (void) syslog(LOG_WARNING, + "t_look(file descriptor %d/transport %s) returned %d not T_UDERR (%d)", + fd, nconf->nc_proto, ret, T_UDERR); + } + + if (uderr == NULL) { + /* LINTED pointer alignment */ + uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL); + if (uderr == NULL) { + if (t_errno == TSYSERR) { + /* + * Save the error code across + * syslog(), just in case + * syslog() gets its own error + * and therefore overwrites errno. + */ + error = errno; + (void) syslog(LOG_ERR, + "t_alloc(file descriptor %d/transport %s, T_UDERROR) failed: %m", + fd, nconf->nc_proto); + return (error); + } + (void) syslog(LOG_ERR, +"t_alloc(file descriptor %d/transport %s, T_UDERROR) failed TLI error: %d", + fd, nconf->nc_proto, t_errno); + goto flush_it; + } + } + + ret = t_rcvuderr(fd, uderr); + if (ret == 0) { + + /* + * Save the datagram error in errno, so that the + * %m argument to syslog picks up the error string. + */ + errno = uderr->error; + + /* + * Log the datagram error, then log the host that + * probably triggerred. Cannot log both in the + * same transaction because of packet size limitations + * in /dev/log. + */ + (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, +"NFS response over <file descriptor %d/transport %s> generated error: %m", + fd, nconf->nc_proto); + + /* + * Try to map the client's address back to a + * name. + */ + ret = netdir_getbyaddr(nconf, &host, &uderr->addr); + if (ret != -1 && host && host->h_cnt > 0 && + host->h_hostservs) { + (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, +"Bad NFS response was sent to client with host name: %s; service port: %s", + host->h_hostservs->h_host, + host->h_hostservs->h_serv); + } else { + int i, j; + char *buf; + char *hex = "0123456789abcdef"; + + /* + * Mapping failed, print the whole thing + * in ASCII hex. + */ + buf = (char *)malloc(uderr->addr.len * 2 + 1); + for (i = 0, j = 0; i < uderr->addr.len; i++, j += 2) { + buf[j] = hex[((uderr->addr.buf[i]) >> 4) & 0xf]; + buf[j+1] = hex[uderr->addr.buf[i] & 0xf]; + } + buf[j] = '\0'; + (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, + "Bad NFS response was sent to client with transport address: 0x%s", + buf); + free((void *)buf); + } + + if (ret == 0 && host != NULL) + netdir_free((void *)host, ND_HOSTSERVLIST); + return (0); + } + + switch (t_errno) { + case TNOUDERR: + goto flush_it; + case TSYSERR: + /* + * System errors are returned to caller. + * Save the error code across + * syslog(), just in case + * syslog() gets its own error + * and therefore overwrites errno. + */ + error = errno; + (void) syslog(LOG_ERR, + "t_rcvuderr(file descriptor %d/transport %s) %m", + fd, nconf->nc_proto); + return (error); + default: + (void) syslog(LOG_ERR, + "t_rcvuderr(file descriptor %d/transport %s) TLI error %d", + fd, nconf->nc_proto, t_errno); + goto flush_it; + } + +flush_it: + /* + * If we get here, then we could not cope with whatever message + * we attempted to read, so flush it. If we did read a message, + * and one isn't present, that is all right, because fd is in + * nonblocking mode. + */ + (void) syslog(LOG_ERR, + "Flushing one input message from <file descriptor %d/transport %s>", + fd, nconf->nc_proto); + + /* + * Read and discard the message. Do this this until there is + * no more control/data in the message or until we get an error. + */ + do { + ctl->maxlen = sizeof (ctlbuf); + ctl->buf = ctlbuf; + data->maxlen = sizeof (databuf); + data->buf = databuf; + flags = 0; + ret = getmsg(fd, ctl, data, &flags); + if (ret == -1) + return (errno); + } while (ret != 0); + + return (0); +} + +static void +conn_close_oldest(void) +{ + int fd; + int i1; + + /* + * Find the oldest connection that is not already in the + * process of shutting down. + */ + for (i1 = end_listen_fds; /* no conditional expression */; i1++) { + if (i1 >= num_fds) + return; + if (conn_polled[i1].closing == 0) + break; + } +#ifdef DEBUG + printf("too many connections (%d), releasing oldest (%d)\n", + num_conns, poll_array[i1].fd); +#else + syslog(LOG_WARNING, "too many connections (%d), releasing oldest (%d)", + num_conns, poll_array[i1].fd); +#endif + fd = poll_array[i1].fd; + if (conn_polled[i1].nc.nc_semantics == NC_TPI_COTS) { + /* + * For politeness, send a T_DISCON_REQ to the transport + * provider. We close the stream anyway. + */ + (void) t_snddis(fd, (struct t_call *)0); + num_conns--; + remove_from_poll_list(fd); + (void) t_close(fd); + } else { + /* + * For orderly release, we do not close the stream + * until the T_ORDREL_IND arrives to complete + * the handshake. + */ + if (t_sndrel(fd) == 0) + conn_polled[i1].closing = 1; + } +} + +static boolean_t +conn_get(int fd, struct netconfig *nconf, struct conn_ind **connp) +{ + struct conn_ind *conn; + struct conn_ind *next_conn; + + conn = (struct conn_ind *)malloc(sizeof (*conn)); + if (conn == NULL) { + syslog(LOG_ERR, "malloc for listen indication failed"); + return (FALSE); + } + + /* LINTED pointer alignment */ + conn->conn_call = (struct t_call *)t_alloc(fd, T_CALL, T_ALL); + if (conn->conn_call == NULL) { + free((char *)conn); + nfslib_log_tli_error("t_alloc", fd, nconf); + return (FALSE); + } + + if (t_listen(fd, conn->conn_call) == -1) { + nfslib_log_tli_error("t_listen", fd, nconf); + (void) t_free((char *)conn->conn_call, T_CALL); + free((char *)conn); + return (FALSE); + } + + if (conn->conn_call->udata.len > 0) { + syslog(LOG_WARNING, + "rejecting inbound connection(%s) with %d bytes of connect data", + nconf->nc_proto, conn->conn_call->udata.len); + + conn->conn_call->udata.len = 0; + (void) t_snddis(fd, conn->conn_call); + (void) t_free((char *)conn->conn_call, T_CALL); + free((char *)conn); + return (FALSE); + } + + if ((next_conn = *connp) != NULL) { + next_conn->conn_prev->conn_next = conn; + conn->conn_next = next_conn; + conn->conn_prev = next_conn->conn_prev; + next_conn->conn_prev = conn; + } else { + conn->conn_next = conn; + conn->conn_prev = conn; + *connp = conn; + } + return (TRUE); +} + +static int +discon_get(int fd, struct netconfig *nconf, struct conn_ind **connp) +{ + struct conn_ind *conn; + struct t_discon discon; + + discon.udata.buf = (char *)0; + discon.udata.maxlen = 0; + if (t_rcvdis(fd, &discon) == -1) { + nfslib_log_tli_error("t_rcvdis", fd, nconf); + return (-1); + } + + conn = *connp; + if (conn == NULL) + return (0); + + do { + if (conn->conn_call->sequence == discon.sequence) { + if (conn->conn_next == conn) + *connp = (struct conn_ind *)0; + else { + if (conn == *connp) { + *connp = conn->conn_next; + } + conn->conn_next->conn_prev = conn->conn_prev; + conn->conn_prev->conn_next = conn->conn_next; + } + free((char *)conn); + break; + } + conn = conn->conn_next; + } while (conn != *connp); + + return (0); +} + +static void +cots_listen_event(int fd, int conn_index) +{ + struct t_call *call; + struct conn_ind *conn; + struct conn_ind *conn_head; + int event; + struct netconfig *nconf = &conn_polled[conn_index].nc; + int new_fd; + struct netbuf addrmask; + int ret = 0; + char *clnt; + char *clnt_uaddr = NULL; + struct nd_hostservlist *clnt_serv = NULL; + + conn_head = NULL; + (void) conn_get(fd, nconf, &conn_head); + + while ((conn = conn_head) != NULL) { + conn_head = conn->conn_next; + if (conn_head == conn) + conn_head = NULL; + else { + conn_head->conn_prev = conn->conn_prev; + conn->conn_prev->conn_next = conn_head; + } + call = conn->conn_call; + free(conn); + + /* + * If we have already accepted the maximum number of + * connections allowed on the command line, then drop + * the oldest connection (for any protocol) before + * accepting the new connection. Unless explicitly + * set on the command line, max_conns_allowed is -1. + */ + if (max_conns_allowed != -1 && num_conns >= max_conns_allowed) + conn_close_oldest(); + + /* + * Create a new transport endpoint for the same proto as + * the listener. + */ + new_fd = nfslib_transport_open(nconf); + if (new_fd == -1) { + call->udata.len = 0; + (void) t_snddis(fd, call); + (void) t_free((char *)call, T_CALL); + syslog(LOG_ERR, "Cannot establish transport over %s", + nconf->nc_device); + continue; + } + + /* Bind to a generic address/port for the accepting stream. */ + if (t_bind(new_fd, NULL, NULL) == -1) { + nfslib_log_tli_error("t_bind", new_fd, nconf); + call->udata.len = 0; + (void) t_snddis(fd, call); + (void) t_free((char *)call, T_CALL); + (void) t_close(new_fd); + continue; + } + + while (t_accept(fd, new_fd, call) == -1) { + if (t_errno != TLOOK) { +#ifdef DEBUG + nfslib_log_tli_error("t_accept", fd, nconf); +#endif + call->udata.len = 0; + (void) t_snddis(fd, call); + (void) t_free((char *)call, T_CALL); + (void) t_close(new_fd); + goto do_next_conn; + } + while (event = t_look(fd)) { + switch (event) { + case T_LISTEN: +#ifdef DEBUG + printf( +"cots_listen_event(%s): T_LISTEN during accept processing\n", nconf->nc_proto); +#endif + (void) conn_get(fd, nconf, &conn_head); + continue; + case T_DISCONNECT: +#ifdef DEBUG + printf( + "cots_listen_event(%s): T_DISCONNECT during accept processing\n", + nconf->nc_proto); +#endif + (void) discon_get(fd, nconf, + &conn_head); + continue; + default: + syslog(LOG_ERR, + "unexpected event 0x%x during accept processing (%s)", + event, nconf->nc_proto); + call->udata.len = 0; + (void) t_snddis(fd, call); + (void) t_free((char *)call, T_CALL); + (void) t_close(new_fd); + goto do_next_conn; + } + } + } + + if (set_addrmask(new_fd, nconf, &addrmask) < 0) { + (void) syslog(LOG_ERR, + "Cannot set address mask for %s", + nconf->nc_netid); + (void) t_snddis(new_fd, NULL); + (void) t_free((char *)call, T_CALL); + (void) t_close(new_fd); + continue; + } + + /* Tell kRPC about the new stream. */ + if (Mysvc4 != NULL) + ret = (*Mysvc4)(new_fd, &addrmask, nconf, + NFS4_KRPC_START, &call->addr); + else + ret = (*Mysvc)(new_fd, addrmask, nconf); + + if (ret < 0) { + if (errno != ENOTCONN) { + syslog(LOG_ERR, + "unable to register new connection: %m"); + } else { + /* + * This is the only error that could be + * caused by the client, so who was it? + */ + if (netdir_getbyaddr(nconf, &clnt_serv, + &(call->addr)) == ND_OK && + clnt_serv->h_cnt > 0) + clnt = clnt_serv->h_hostservs->h_host; + else + clnt = clnt_uaddr = taddr2uaddr(nconf, + &(call->addr)); + /* + * If we don't know who the client was, + * remain silent. + */ + if (clnt) + syslog(LOG_ERR, +"unable to register new connection: client %s has dropped connection", clnt); + if (clnt_serv) { + netdir_free(clnt_serv, ND_HOSTSERVLIST); + clnt_serv = NULL; + } + if (clnt_uaddr) { + free(clnt_uaddr); + clnt_uaddr = NULL; + } + } + free(addrmask.buf); + (void) t_snddis(new_fd, NULL); + (void) t_free((char *)call, T_CALL); + (void) t_close(new_fd); + goto do_next_conn; + } + + free(addrmask.buf); + (void) t_free((char *)call, T_CALL); + + /* + * Poll on the new descriptor so that we get disconnect + * and orderly release indications. + */ + num_conns++; + add_to_poll_list(new_fd, nconf); + + /* Reset nconf in case it has been moved. */ + nconf = &conn_polled[conn_index].nc; +do_next_conn:; + } +} + +static int +do_poll_cots_action(int fd, int conn_index) +{ + char buf[256]; + int event; + int i1; + int flags; + struct conn_entry *connent = &conn_polled[conn_index]; + struct netconfig *nconf = &(connent->nc); + const char *errorstr; + + while (event = t_look(fd)) { + switch (event) { + case T_LISTEN: +#ifdef DEBUG +printf("do_poll_cots_action(%s,%d): T_LISTEN event\n", nconf->nc_proto, fd); +#endif + cots_listen_event(fd, conn_index); + break; + + case T_DATA: +#ifdef DEBUG +printf("do_poll_cots_action(%d,%s): T_DATA event\n", fd, nconf->nc_proto); +#endif + /* + * Receive a private notification from CONS rpcmod. + */ + i1 = t_rcv(fd, buf, sizeof (buf), &flags); + if (i1 == -1) { + syslog(LOG_ERR, "t_rcv failed"); + break; + } + if (i1 < sizeof (int)) + break; + i1 = BE32_TO_U32(buf); + if (i1 == 1 || i1 == 2) { + /* + * This connection has been idle for too long, + * so release it as politely as we can. If we + * have already initiated an orderly release + * and we get notified that the stream is + * still idle, pull the plug. This prevents + * hung connections from continuing to consume + * resources. + */ +#ifdef DEBUG +printf("do_poll_cots_action(%s,%d): ", nconf->nc_proto, fd); +printf("initiating orderly release of idle connection\n"); +#endif + if (nconf->nc_semantics == NC_TPI_COTS || + connent->closing != 0) { + (void) t_snddis(fd, (struct t_call *)0); + goto fdclose; + } + /* + * For NC_TPI_COTS_ORD, the stream is closed + * and removed from the poll list when the + * T_ORDREL is received from the provider. We + * don't wait for it here because it may take + * a while for the transport to shut down. + */ + if (t_sndrel(fd) == -1) { + syslog(LOG_ERR, + "unable to send orderly release %m"); + } + connent->closing = 1; + } else + syslog(LOG_ERR, + "unexpected event from CONS rpcmod %d", i1); + break; + + case T_ORDREL: +#ifdef DEBUG +printf("do_poll_cots_action(%s,%d): T_ORDREL event\n", nconf->nc_proto, fd); +#endif + /* Perform an orderly release. */ + if (t_rcvrel(fd) == 0) { + /* T_ORDREL on listen fd's should be ignored */ + if (!is_listen_fd_index(conn_index)) { + (void) t_sndrel(fd); + goto fdclose; + } + break; + + } else if (t_errno == TLOOK) { + break; + } else { + nfslib_log_tli_error("t_rcvrel", fd, nconf); + + /* + * check to make sure we do not close + * listen fd + */ + if (is_listen_fd_index(conn_index)) + break; + else + goto fdclose; + } + + case T_DISCONNECT: +#ifdef DEBUG +printf("do_poll_cots_action(%s,%d): T_DISCONNECT event\n", nconf->nc_proto, fd); +#endif + if (t_rcvdis(fd, (struct t_discon *)NULL) == -1) + nfslib_log_tli_error("t_rcvdis", fd, nconf); + + /* + * T_DISCONNECT on listen fd's should be ignored. + */ + if (is_listen_fd_index(conn_index)) + break; + else + goto fdclose; + + default: + if (t_errno == TSYSERR) { + if ((errorstr = strerror(errno)) == NULL) { + (void) sprintf(buf, + "Unknown error num %d", errno); + errorstr = (const char *) buf; + } + } else if (event == -1) + errorstr = t_strerror(t_errno); + else + errorstr = ""; + syslog(LOG_ERR, + "unexpected TLI event (0x%x) on " + "connection-oriented transport(%s,%d):%s", + event, nconf->nc_proto, fd, errorstr); +fdclose: + num_conns--; + remove_from_poll_list(fd); + (void) t_close(fd); + return (0); + } + } + + return (0); +} + +static char * +serv_name_to_port_name(char *name) +{ + /* + * Map service names (used primarily in logging) to + * RPC port names (used by netdir_*() routines). + */ + if (strcmp(name, "NFS") == 0) { + return ("nfs"); + } else if (strcmp(name, "NLM") == 0) { + return ("lockd"); + } else if (strcmp(name, "NFS4_CALLBACK") == 0) { + return ("nfs4_callback"); + } + + return ("unrecognized"); +} + +int +nlm_bind_to_provider(char *provider, struct netbuf **addr, + struct netconfig **retnconf) +{ + int i, res; + struct netconfig *nconf = NULL, *np; + + for (i = 0; i < N_NETCONF_ENTS; i++) { + np = &nca[i]; + + if (strcmp(np->nc_device, provider) != 0) + continue; + + /* Construct our own netconfig */ + if ((nconf = calloc(1, sizeof (struct netconfig))) == NULL) + goto out; + + nconf->nc_semantics = np->nc_semantics; + if ((nconf->nc_netid = strdup(np->nc_netid)) == NULL) + goto out; + if ((nconf->nc_protofmly = strdup(np->nc_protofmly)) == NULL) + goto out; + if ((nconf->nc_proto = strdup(np->nc_proto)) == NULL) + goto out; + if ((nconf->nc_device = strdup(np->nc_device)) == NULL) + goto out; + + *retnconf = nconf; + + /* + * Passing addr == NULL implies we skip the bind since we only + * need the netconfig for unregistering from rpcbind. + */ + if (addr == NULL) + return (0); + if ((res = nlm_bindit(nconf, addr, listen_backlog)) == -1) + freenetconfigent(nconf); + return (res); + } + +out: + if (nconf != NULL) + freenetconfigent(nconf); + syslog(LOG_ERR, "couldn't find netconfig entry for provider %s", + provider); + return (-1); +} + +static int +bind_to_proto(NETSELDECL(proto), char *serv, struct netbuf **addr, + struct netconfig **retnconf) +{ + struct netconfig *nconf; + NCONF_HANDLE *nc = NULL; + struct nd_hostserv hs; + + hs.h_host = HOST_SELF; + hs.h_serv = serv_name_to_port_name(serv); + + if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) { + syslog(LOG_ERR, "setnetconfig failed: %m"); + return (-1); + } + while (nconf = getnetconfig(nc)) { + if (OK_TPI_TYPE(nconf) && NETSELEQ(nconf->nc_proto, proto)) { + *retnconf = nconf; + return (nfslib_bindit(nconf, addr, &hs, + listen_backlog)); + } + } + (void) endnetconfig(nc); + + syslog(LOG_ERR, "couldn't find netconfig entry for protocol %s", + proto); + return (-1); +} + +#include <netinet/in.h> + +/* + * Create an address mask appropriate for the transport. + * The mask is used to obtain the host-specific part of + * a network address when comparing addresses. + * For an internet address the host-specific part is just + * the 32 bit IP address and this part of the mask is set + * to all-ones. The port number part of the mask is zeroes. + */ +static int +set_addrmask(int fd, struct netconfig *nconf, struct netbuf *mask) +{ + struct t_info info; + + /* + * Find the size of the address we need to mask. + */ + if (t_getinfo(fd, &info) < 0) { + t_error("t_getinfo"); + return (-1); + } + mask->len = mask->maxlen = info.addr; + if (info.addr <= 0) { + /* + * loopback devices have infinite addr size + * (it is identified by -1 in addr field of t_info structure), + * so don't build the netmask for them. It's a special case + * that should be handled properly. + */ + if ((info.addr == -1) && + (0 == strcmp(nconf->nc_protofmly, NC_LOOPBACK))) { + memset(mask, 0, sizeof (*mask)); + return (0); + } + + syslog(LOG_ERR, "set_addrmask: address size: %ld", info.addr); + return (-1); + } + + mask->buf = (char *)malloc(mask->len); + if (mask->buf == NULL) { + syslog(LOG_ERR, "set_addrmask: no memory"); + return (-1); + } + (void) memset(mask->buf, 0, mask->len); /* reset all mask bits */ + + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { + /* + * Set the mask so that the port is ignored. + */ + /* LINTED pointer alignment */ + ((struct sockaddr_in *)mask->buf)->sin_addr.s_addr = + (ulong_t)~0; + /* LINTED pointer alignment */ + ((struct sockaddr_in *)mask->buf)->sin_family = + (ushort_t)~0; + } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { + /* LINTED pointer alignment */ + (void) memset(&((struct sockaddr_in6 *)mask->buf)->sin6_addr, + (uchar_t)~0, sizeof (struct in6_addr)); + /* LINTED pointer alignment */ + ((struct sockaddr_in6 *)mask->buf)->sin6_family = + (ushort_t)~0; + } else { + + /* + * Set all mask bits. + */ + (void) memset(mask->buf, 0xFF, mask->len); + } + return (0); +} + +/* + * For listen fd's index is always less than end_listen_fds. + * end_listen_fds is defined externally in the daemon that uses this library. + * It's value is equal to the number of open file descriptors after the + * last listen end point was opened but before any connection was accepted. + */ +static int +is_listen_fd_index(int index) +{ + return (index < end_listen_fds); +} diff --git a/usr/src/lib/brand/lx/lx_support/Makefile b/usr/src/lib/brand/lx/lx_support/Makefile new file mode 100644 index 0000000000..094f63d16a --- /dev/null +++ b/usr/src/lib/brand/lx/lx_support/Makefile @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2019 Joyent, Inc. + +PROG = lx_support +PROGS = $(PROG) +OBJS = lx_support + +all: $(PROG) + +include ../Makefile.lx +include $(SRC)/cmd/Makefile.cmd + +# override the install directory +ROOTBIN = $(ROOTBRANDDIR) +CLOBBERFILES = $(OBJS) $(ROOTPROGS) + +UTSBASE = $(SRC)/uts + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I$(UTSBASE)/common/brand/lx +LDLIBS += -lzonecfg + +SMOFF += signed + +.KEEP_STATE: + +install: all $(ROOTPROGS) + +clean: + $(RM) $(PROG) $(OBJS) + +lint: lint_PROG + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/lx/lx_support/lx_support.c b/usr/src/lib/brand/lx/lx_support/lx_support.c new file mode 100644 index 0000000000..d35c68ed8d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_support/lx_support.c @@ -0,0 +1,392 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. + */ + +/* + * lx_support is a small cli utility used to perform some brand-specific + * tasks when booting, halting, or verifying a zone. This utility is not + * intended to be called by users - it is intended to be invoked by the + * zones utilities. + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stropts.h> +#include <sys/ioccom.h> +#include <sys/stat.h> +#include <sys/systeminfo.h> +#include <sys/types.h> +#include <sys/varargs.h> +#include <unistd.h> +#include <libintl.h> +#include <locale.h> + +#include <libzonecfg.h> +#include <sys/lx_brand.h> + +static void lxs_err(char *msg, ...) __NORETURN; +static void usage(void) __NORETURN; + +#define CP_CMD "/usr/bin/cp" +#define MOUNT_CMD "/sbin/mount" + +static char *bname = NULL; +static char *zonename = NULL; +static char *zoneroot = NULL; + +#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ +#endif + +static void +lxs_err(char *msg, ...) +{ + char buf[1024]; + va_list ap; + + va_start(ap, msg); + /*LINTED*/ + (void) vsnprintf(buf, sizeof (buf), msg, ap); + va_end(ap); + + (void) printf("%s error: %s\n", bname, buf); + + exit(1); + /*NOTREACHED*/ +} + +/* + * Cleanup from earlier versions of the code which created a /dev/initctl FIFO. + */ +static void +lxs_remove_initctl() +{ + char special[MAXPATHLEN]; + + if (snprintf(special, sizeof (special), "%s/dev/initctl", zoneroot) >= + sizeof (special)) + lxs_err("%s: %s", gettext("Failed to cleanup /dev/initctl"), + gettext("zoneroot is too long")); + + (void) unlink(special); +} + +/* + * fsck gets really confused when run inside a zone. Removing this file + * prevents it from running + */ +static void +lxs_remove_autofsck() +{ + char path[MAXPATHLEN]; + int err; + + if (snprintf(path, MAXPATHLEN, "%s/root/.autofsck", zoneroot) >= + MAXPATHLEN) + lxs_err("%s: %s", gettext("Failed to remove /.autofsck"), + gettext("zoneroot is too long")); + + if (unlink(path) < 0) { + err = errno; + if (err != ENOENT) + lxs_err("%s: %s", + gettext("Failed to remove /.autofsck"), + strerror(err)); + } +} + +/* + * Extract any lx-supported attributes from the zone configuration file. + */ +static void +lxs_getattrs(zone_dochandle_t zdh, char **krelease) +{ + struct zone_attrtab attrtab; + int err; + + /* initialize the attribute iterator */ + if (zonecfg_setattrent(zdh) != Z_OK) { + zonecfg_fini_handle(zdh); + lxs_err(gettext("error accessing zone configuration")); + } + + *krelease = (char *)malloc(LX_KERN_RELEASE_MAX); + if (*krelease == NULL) + lxs_err(gettext("out of memory")); + + bzero(*krelease, LX_KERN_RELEASE_MAX); + while ((err = zonecfg_getattrent(zdh, &attrtab)) == Z_OK) { + if ((strcmp(attrtab.zone_attr_name, "kernel-version") == 0) && + (zonecfg_get_attr_string(&attrtab, *krelease, + LX_KERN_RELEASE_MAX) != Z_OK)) + lxs_err(gettext("invalid type for zone attribute: %s"), + attrtab.zone_attr_name); + } + + if (strlen(*krelease) == 0) { + free(*krelease); + *krelease = NULL; + } + + /* some kind of error while looking up attributes */ + if (err != Z_NO_ENTRY) + lxs_err(gettext("error accessing zone configuration")); +} + +/* + * Attempt to lookup the "tty" gid from within the zone's /etc/group file. + * The gid is used to emulate existing Linux udev behavior for setting the gid + * on a pty. If we cannot lookup the gid for some reason, we still allow + * the zone to boot. + * + * Because we're reading the lx zone's /etc/group file, we have to parse it + * ourselves, but this is simple since we only need the gid. + */ +static void +lxs_set_ttygid(zoneid_t zoneid) +{ + char buf[MAXPATHLEN]; + FILE *fp; + gid_t tty_gid = 0; + + if (snprintf(buf, sizeof (buf), "%s/root/etc/group", zoneroot) >= + sizeof (buf)) + return; + + if ((fp = fopen(buf, "r")) == NULL) + return; + + /* + * Look for the "tty" line and get the gid. Note that this loop will + * properly handle a long line that won't fit into buf on a single + * fgets. The subsequent fgets will consume more of the line but we + * continue on through that. In practice this is unlikely to occur. + */ + while (fgets(buf, sizeof (buf), fp) != NULL) { + char *p, *p_id; + long val; + + /* group name */ + if ((p = strchr(buf, ':')) == NULL) + continue; + *p = '\0'; + if (strcmp(buf, "tty") != 0) + continue; + + /* found tty entry, skip the "x" field */ + if ((p = strchr(p + 1, ':')) == NULL) + goto done; + /* gid field */ + p_id = p + 1; + if ((p = strchr(p_id, ':')) == NULL) + goto done; + *p = '\0'; + + errno = 0; + val = strtol(p_id, &p, 10); + /* Perform simple validation on the gid */ + if (errno != 0 || *p != '\0' || val > MAXUID || val < 0) + goto done; + tty_gid = (gid_t)val; + break; + } + + if (tty_gid != 0) + (void) zone_setattr(zoneid, LX_ATTR_TTY_GID, &tty_gid, + sizeof (tty_gid)); + +done: + (void) fclose(fp); +} + +static int +lxs_boot() +{ + zoneid_t zoneid; + zone_dochandle_t zdh; + char *krelease; + + lxs_remove_initctl(); + lxs_remove_autofsck(); + + if ((zdh = zonecfg_init_handle()) == NULL) + lxs_err(gettext("unable to initialize zone handle")); + + if (zonecfg_get_handle((char *)zonename, zdh) != Z_OK) { + zonecfg_fini_handle(zdh); + lxs_err(gettext("unable to load zone configuration")); + } + + /* Extract any relevant attributes from the config file. */ + lxs_getattrs(zdh, &krelease); + zonecfg_fini_handle(zdh); + + /* + * Let the kernel know whether or not this zone's init process + * should be automatically restarted on its death. + */ + if ((zoneid = getzoneidbyname(zonename)) < 0) + lxs_err(gettext("unable to get zoneid")); + + if (krelease != NULL) { + /* Backward compatability with incomplete version attr */ + if (strcmp(krelease, "2.4") == 0) { + krelease = "2.4.21"; + } else if (strcmp(krelease, "2.6") == 0) { + krelease = "2.6.18"; + } + + if (zone_setattr(zoneid, LX_ATTR_KERN_RELEASE, krelease, + strlen(krelease)) < 0) + lxs_err(gettext("unable to set kernel version")); + } + + lxs_set_ttygid(zoneid); + + return (0); +} + +static int +lxs_halt() +{ + return (0); +} + +static int +lxs_verify(char *xmlfile) +{ + zone_dochandle_t handle; + char *krelease; + char hostidp[HW_HOSTID_LEN]; + zone_iptype_t iptype; + + if ((handle = zonecfg_init_handle()) == NULL) + lxs_err(gettext("internal libzonecfg.so.1 error"), 0); + + if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) { + zonecfg_fini_handle(handle); + lxs_err(gettext("zonecfg provided an invalid XML file")); + } + + /* + * Check to see whether the zone has hostid emulation enabled. + */ + if (zonecfg_get_hostid(handle, hostidp, sizeof (hostidp)) == Z_OK) { + zonecfg_fini_handle(handle); + lxs_err(gettext("lx zones do not support hostid emulation")); + } + + /* + * Only exclusive stack is supported. + */ + if (zonecfg_get_iptype(handle, &iptype) != Z_OK || + iptype != ZS_EXCLUSIVE) { + zonecfg_fini_handle(handle); + lxs_err(gettext("lx zones do not support shared IP stacks")); + } + + /* Extract any relevant attributes from the config file. */ + lxs_getattrs(handle, &krelease); + zonecfg_fini_handle(handle); + + if (krelease) { + char *pdot, *ep; + long major_ver; + + pdot = strchr(krelease, '.'); + if (pdot != NULL) + *pdot = '\0'; + errno = 0; + major_ver = strtol(krelease, &ep, 10); + if (major_ver < 2 || errno != 0 || *ep != '\0') + lxs_err(gettext("invalid value for zone attribute: %s"), + "kernel-version"); + if (pdot != NULL) + *pdot = '.'; + + } + return (0); +} + +static void +usage() +{ + + (void) fprintf(stderr, + gettext("usage:\t%s boot <zoneroot> <zonename>\n"), bname); + (void) fprintf(stderr, + gettext(" \t%s halt <zoneroot> <zonename>\n"), bname); + (void) fprintf(stderr, + gettext(" \t%s verify <xml file>\n\n"), bname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + bname = basename(argv[0]); + + if (argc < 3) + usage(); + + if (strcmp(argv[1], "boot") == 0) { + if (argc != 4) + lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"), + bname, argv[1]); + zoneroot = argv[2]; + zonename = argv[3]; + return (lxs_boot()); + } + + if (strcmp(argv[1], "halt") == 0) { + if (argc != 4) + lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"), + bname, argv[1]); + zoneroot = argv[2]; + zonename = argv[3]; + return (lxs_halt()); + } + + if (strcmp(argv[1], "verify") == 0) { + if (argc != 3) + lxs_err(gettext("usage: %s verify <xml file>"), + bname); + return (lxs_verify(argv[2])); + } + + usage(); + /*NOTREACHED*/ +} diff --git a/usr/src/lib/brand/lx/lx_vdso/Makefile b/usr/src/lib/brand/lx/lx_vdso/Makefile new file mode 100644 index 0000000000..56dd0a7a3c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/Makefile @@ -0,0 +1,39 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +include ../../../Makefile.lib + +SUBDIRS = tools $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all install clean clobber: $(SUBDIRS) + +lint: $(LINT_SUBDIRS) + +$(MACH): tools +$(MACH64): tools + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/brand/lx/lx_vdso/Makefile.com b/usr/src/lib/brand/lx/lx_vdso/Makefile.com new file mode 100644 index 0000000000..9a1836112c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/Makefile.com @@ -0,0 +1,85 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2019, Joyent, Inc. +# + +LIBRARY = lx_vdso.a +VERS = .1 + +include $(SRC)/lib/commpage/Makefile.shared.com + +COBJS = vdso_main.o vdso_subr.o +OBJECTS = $(COBJS) $(COMMPAGE_OBJS) + +include ../../../../Makefile.lib +include ../../Makefile.lx + +# +# Since our name doesn't start with "lib", Makefile.lib incorrectly +# calculates LIBNAME. Therefore, we set it here. +# +LIBNAME = lx_vdso + +MAPFILES = ../common/mapfile-vers +MAPOPTS = $(MAPFILES:%=-M%) + +ASOBJS = vdso_subr.o +COBJS = vdso_main.o +OBJECTS = $(ASOBJS) $(COBJS) $(COMMPAGE_OBJS) + +SRCDIR = ../common + +ASSRCS = $(ASOBJS:%.o=$(ISASRCDIR)/%.s) +CSRCS = $(COBJS:%.o=$(SRCDIR)/%.c) +SRCS = $(ASSRCS) $(CSRCS) + +LIBS = $(DYNLIB) +DYNFLAGS += $(DYNFLAGS_$(CLASS)) +DYNFLAGS += $(MAPOPTS) +LDLIBS += +ASFLAGS = -P $(ASFLAGS_$(CURTYPE)) -D_ASM + + +LIBS = $(DYNLIB) + +CLEANFILES += $(DYNLIB) +ROOTLIBDIR = $(ROOT)/usr/lib/brand/lx +ROOTLIBDIR64 = $(ROOT)/usr/lib/brand/lx/$(MACH64) + +VDSO_TOOL = ../tools/vdso_tool + +.KEEP_STATE: + +# +# While $(VDSO_TOOL) performs most of the transformations required to +# construct a correct VDSO object, we still make use of $(ELFEDIT). To +# remove the $(ELFEDIT) requirement would mean shouldering the burden of +# becoming a link-editor; this dark lore is best left to the linker aliens. +# +all: $(LIBS) + $(ELFEDIT) -e "dyn:value -add VERSYM $$($(ELFEDIT) \ + -e 'shdr:dump .SUNW_versym' $(DYNLIB) | \ + $(AWK) '{ if ($$1 == "sh_addr:") { print $$2 } }')" $(DYNLIB) + $(VDSO_TOOL) -f $(DYNLIB) + +lint: $(LINTLIB) lintcheck + +include ../../../../Makefile.targ +include $(SRC)/lib/commpage/Makefile.shared.targ + +pics/%.o: $(ISASRCDIR)/%.s + $(COMPILE.s) -o $@ $< + $(POST_PROCESS_S_O) + +pics/vdso_main.o := CPPFLAGS += $(COMMPAGE_CPPFLAGS) -I$(SRCDIR) +pics/vdso_subr.o := ASFLAGS += -I$(SRC)/uts/common/brand/lx -I$(SRCDIR) diff --git a/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile b/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile new file mode 100644 index 0000000000..1a12492a97 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +ISASRCDIR=. +TARGET_ARCH=$(MACH64) + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +ASFLAGS += -D__$(MACH64) + +SONAME = linux-vdso.so.1 + +# Disable save-args since some vDSO consumers are sensitive to stack usage. +SAVEARGS = + +# +# You might ask, why aren't we overriding BUILD.SO in Makefile.com. +# That's a sad story. The answer is that Makefile.lib.64 includes +# Makefile.master.64 which redefines BUILD.SO, leaving us in an +# unfortunate jumble. Therefore we have to redefine it in the +# lower-level Makefile. +# +BUILD.SO = $(LD) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS) + +ASSYMDEP_OBJS = lx_vdso.o + +CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB) $(ROOTLIBDIR64)/$(LINTLIB) + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s b/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s new file mode 100644 index 0000000000..592884aa32 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s @@ -0,0 +1,131 @@ +/* + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + + +#include <sys/asm_linkage.h> +#include <sys/lx_syscalls.h> +#include <vdso_defs.h> + +#if defined(lint) + +comm_page_t * +__vdso_find_commpage() +{} + +long +__vdso_sys_clock_gettime(uint_t clock_id, timespec_t *tp) +{} + +int +__vdso_sys_gettimeofday(timespec_t *tp, struct lx_timezone *tz) +{} + +time_t +__vdso_sys_time(timespec_t *tp) +{} + +#else /* lint */ + + ENTRY_NP(__vdso_find_commpage) + leaq 0x0(%rip), %rax + andq $LX_VDSO_ADDR_MASK, %rax + addq $LX_VDSO_SIZE, %rax + ret + SET_SIZE(__vdso_find_commpage) + + ENTRY_NP(__vdso_sys_clock_gettime) + movl $LX_SYS_clock_gettime, %eax + syscall + ret + SET_SIZE(__vdso_sys_clock_gettime) + + ENTRY_NP(__vdso_sys_gettimeofday) + movl $LX_SYS_gettimeofday, %eax + syscall + ret + SET_SIZE(__vdso_sys_gettimeofday) + + ENTRY_NP(__vdso_sys_time) + movl $LX_SYS_time, %eax + syscall + ret + SET_SIZE(__vdso_sys_time) + +/* + * long + * __vdso_clock_gettime(uint_t, timespec_t *) + */ + ENTRY_NP(__vdso_clock_gettime) + subq $0x18, %rsp + movl %edi, (%rsp) + movq %rsi, 0x8(%rsp) + + call __vdso_find_commpage + movq %rax, 0x10(%rsp) + + movq %rax, %rdi + call __cp_can_gettime + cmpl $0, %eax + je 5f + + /* + * Restore the original args/stack (with commpage pointer in rdx) + * This enables the coming tail-call to the desired function, be it + * __cp_clock_gettime_* or __vdso_sys_clock_gettime. + */ + movl (%rsp), %edi + movq 0x8(%rsp), %rsi + movq 0x10(%rsp), %rdx + addq $0x18, %rsp + + cmpl $LX_CLOCK_REALTIME, %edi + jne 2f +1: + movq %rdx, %rdi + jmp __cp_clock_gettime_realtime + +2: + cmpl $LX_CLOCK_MONOTONIC, %edi + jne 4f +3: + movq %rdx, %rdi + jmp __cp_clock_gettime_monotonic + +4: + cmpl $LX_CLOCK_REALTIME_COARSE, %edi + je 1b + cmpl $LX_CLOCK_MONOTONIC_RAW, %edi + je 3b + cmpl $LX_CLOCK_MONOTONIC_COARSE, %edi + je 3b + jmp 6f + +5: + /* + * When falling through from a failed cp_can_gettime, the stack + * allocation must be released before a tail-call is made to the + * fallback syscall function. + */ + addq $0x18, %rsp + +6: + /* Let the real syscall handle all other cases */ + jmp __vdso_sys_clock_gettime + SET_SIZE(__vdso_clock_gettime) + + +#endif /* lint */ diff --git a/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers b/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers new file mode 100644 index 0000000000..11690ce7d6 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers @@ -0,0 +1,59 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2014 Joyent, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION LINUX_2.6 { + global: + __vdso_gettimeofday; + __vdso_clock_gettime; + __vdso_getcpu; + __vdso_time; + local: + *; +}; + +# +# The vDSO module in GNU/Linux must only have a single PT_LOAD section. +# Further, we should not have any data sections at all. Therefore, we go +# through and explicitly disable several of the writeable sections that +# might commonly show up. +# +LOAD_SEGMENT data { + disable; +}; + +LOAD_SEGMENT ldata { + disable; +}; + +LOAD_SEGMENT bss { + disable; +}; + diff --git a/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h b/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h new file mode 100644 index 0000000000..dfac918a53 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h @@ -0,0 +1,42 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +#ifndef _VDSO_DEFS_H_ +#define _VDSO_DEFS_H_ + +#define LX_CLOCK_REALTIME 0 /* CLOCK_REALTIME */ +#define LX_CLOCK_MONOTONIC 1 /* CLOCK_HIGHRES */ +#define LX_CLOCK_PROCESS_CPUTIME_ID 2 /* Emulated */ +#define LX_CLOCK_THREAD_CPUTIME_ID 3 /* Emulated */ +#define LX_CLOCK_MONOTONIC_RAW 4 /* CLOCK_HIGHRES */ +#define LX_CLOCK_REALTIME_COARSE 5 /* CLOCK_REALTIME */ +#define LX_CLOCK_MONOTONIC_COARSE 6 /* CLOCK_HIGHRES */ + +#if !defined(_ASM) + +struct lx_timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +/* Functions provided by the mach-specific vdso_subr.s */ +extern comm_page_t *__vdso_find_commpage(); +extern int __vdso_sys_gettimeofday(timespec_t *, struct lx_timezone *); +extern time_t __vdso_sys_time(time_t *); +extern long __vdso_sys_clock_gettime(uint_t, timespec_t *); + +#endif /* !defined(_ASM) */ + +#endif /* _VDSO_DEFS_H_ */ diff --git a/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c b/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c new file mode 100644 index 0000000000..40528a152b --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c @@ -0,0 +1,139 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <cp_defs.h> +#include <vdso_defs.h> + + +#if defined(__i386) + +long +__vdso_clock_gettime(uint_t clock_id, timespec_t *tp) +{ + comm_page_t *cp = __vdso_find_commpage(); + + if (__cp_can_gettime(cp) == 0) { + return (__vdso_sys_clock_gettime(clock_id, tp)); + } + + switch (clock_id) { + case LX_CLOCK_REALTIME: + case LX_CLOCK_REALTIME_COARSE: + return (__cp_clock_gettime_realtime(cp, tp)); + + case LX_CLOCK_MONOTONIC: + case LX_CLOCK_MONOTONIC_RAW: + case LX_CLOCK_MONOTONIC_COARSE: + return (__cp_clock_gettime_monotonic(cp, tp)); + + case LX_CLOCK_PROCESS_CPUTIME_ID: + case LX_CLOCK_THREAD_CPUTIME_ID: + default: + return (__vdso_sys_clock_gettime(clock_id, tp)); + } +} + +/* + * On i386, the implementation of __cp_clock_gettime_monotonic expects that an + * hrt2ts function is provided. It is provided below since the vDSO is + * operating on its own, without native libc. + */ +void +hrt2ts(hrtime_t hrt, timespec_t *tsp) +{ + uint32_t sec, nsec, tmp; + + tmp = (uint32_t)(hrt >> 30); + sec = tmp - (tmp >> 2); + sec = tmp - (sec >> 5); + sec = tmp + (sec >> 1); + sec = tmp - (sec >> 6) + 7; + sec = tmp - (sec >> 3); + sec = tmp + (sec >> 1); + sec = tmp + (sec >> 3); + sec = tmp + (sec >> 4); + tmp = (sec << 7) - sec - sec - sec; + tmp = (tmp << 7) - tmp - tmp - tmp; + tmp = (tmp << 7) - tmp - tmp - tmp; + nsec = (uint32_t)hrt - (tmp << 9); + while (nsec >= NANOSEC) { + nsec -= NANOSEC; + sec++; + } + tsp->tv_sec = (time_t)sec; + tsp->tv_nsec = nsec; +} + +#else + +/* + * On amd64, the __vdso_clock_gettime function is implemented in asm to stay + * within the allowed stack budget. + */ + +#endif /* defined(__i386) */ + + +int +__vdso_gettimeofday(timespec_t *tp, struct lx_timezone *tz) +{ + if (tz != NULL) { + tz->tz_minuteswest = 0; + tz->tz_dsttime = 0; + } + + if (tp != NULL) { + comm_page_t *cp = __vdso_find_commpage(); + + if (__cp_can_gettime(cp) == 0) { + return (__vdso_sys_gettimeofday(tp, tz)); + } + + (void) __cp_clock_gettime_realtime(cp, tp); + tp->tv_nsec /= 1000; + } + return (0); +} + +time_t +__vdso_time(time_t *tp) +{ + comm_page_t *cp = __vdso_find_commpage(); + timespec_t ts; + + if (__cp_can_gettime(cp) == 0) { + return (__vdso_sys_time(tp)); + } + + (void) __cp_clock_gettime_realtime(cp, &ts); + if (tp != NULL) { + *tp = ts.tv_sec; + } + return (ts.tv_sec); +} + +int +__vdso_getcpu(uint_t *cpu, uint_t *node, void *tcache) +{ + comm_page_t *cp = __vdso_find_commpage(); + + if (cpu != NULL) { + *cpu = __cp_getcpu(cp); + } + if (node != NULL) { + *node = 0; + } + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_vdso/i386/Makefile b/usr/src/lib/brand/lx/lx_vdso/i386/Makefile new file mode 100644 index 0000000000..7f9a6a13e6 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/i386/Makefile @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +ISASRCDIR=. +TARGET_ARCH=$(MACH) + +include ../Makefile.com + +ASFLAGS += -D__$(MACH) + +SONAME = linux-gate.so.1 + +# +# You might ask, why aren't we overriding BUILD.SO in Makefile.com. +# See the amd64 Makefile for more answers +# +BUILD.SO = $(LD) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS) + +ASSYMDEP_OBJS = lx_vdso.o + +CLOBBERFILES = $(ROOTLIBDIR)/$(DYNLIB) $(ROOTLIBDIR)/$(LINTLIB) + +# Set the object entry point for __vsyscall-ers +entryfix: $(DYNLIB) + $(ELFEDIT) -e "ehdr:e_entry \ + $$($(ELFEDIT) -re 'sym:st_value -osimple __vsyscall' $(DYNLIB))" \ + $(DYNLIB) + +all: entryfix + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s b/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s new file mode 100644 index 0000000000..ed7be8bb23 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s @@ -0,0 +1,92 @@ +/* + * + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + * + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + + +#include <sys/asm_linkage.h> +#include <sys/lx_syscalls.h> + + +#if defined(lint) + +comm_page_t * +__vdso_find_commpage() +{} + +long +__vdso_sys_clock_gettime(uint_t clock_id, timespec_t *tp) +{} + +int +__vdso_sys_gettimeofday(timespec_t *tp, struct lx_timezone *tz) +{} + +time_t +__vdso_sys_time(timespec_t *tp) +{} + +#else /* lint */ + + ENTRY_NP(__vdso_find_commpage) + call 1f +1: popl %eax + andl $LX_VDSO_ADDR_MASK, %eax + addl $LX_VDSO_SIZE, %eax + ret + SET_SIZE(__vdso_find_commpage) + + ENTRY_NP(__vdso_sys_clock_gettime) + movl $LX_SYS_clock_gettime, %eax + movl 0x4(%esp), %ebx + movl 0x8(%esp), %ecx + int $0x80 + ret + SET_SIZE(__vdso_sys_clock_gettime) + + ENTRY_NP(__vdso_sys_gettimeofday) + movl $LX_SYS_gettimeofday, %eax + movl 0x4(%esp), %ebx + movl 0x8(%esp), %ecx + int $0x80 + ret + SET_SIZE(__vdso_sys_gettimeofday) + + ENTRY_NP(__vdso_sys_time) + movl $LX_SYS_time, %eax + movl 0x4(%esp), %ebx + int $0x80 + ret + SET_SIZE(__vdso_sys_time) + + ENTRY_NP(__vsyscall) + /* + * On 32-bit Linux, the VDSO entry point (specified by e_entry) + * provides a potentially accelerated means to vector into the kernel. + * Normally this means using 'sysenter' with a Linux-custom calling + * convention so programs expecting int80 behavior are not required to + * change how arguments are passed. + * + * The SunOS sysenter entry point does _not_ tolerate such a departure + * from convention, so if this function is updated to use sysenter, it + * must properly marshal arguments onto the stack from the int80 style. + * Such an enhancement can only occur once sysenter receives the same + * branding hooks as syscall and int80. + */ + int $0x80 + ret + SET_SIZE(__vsyscall) + +#endif /* lint */ diff --git a/usr/src/lib/brand/lx/lx_vdso/tools/Makefile b/usr/src/lib/brand/lx/lx_vdso/tools/Makefile new file mode 100644 index 0000000000..7907aa67c4 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/tools/Makefile @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +PROG = vdso_tool + +include ../../../../../cmd/Makefile.cmd + +OBJS = vdso_tool.o + +CLOBBERFILES += $(PROG) + +NATIVECC_CFLAGS += $(CFLAGS) $(CCVERBOSE) +NATIVECC_LDLIBS += -lelf + +.KEEP_STATE: + +all: $(PROG) + +install: all + +lint: lint_PROG + +clean: + $(RM) $(OBJS) + +$(PROG): $(OBJS) + $(NATIVECC) $(NATIVECC_CFLAGS) $(NATIVECC_LDLIBS) $(OBJS) -o $@ + $(POST_PROCESS) + +include ../../../../../cmd/Makefile.targ diff --git a/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c b/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c new file mode 100644 index 0000000000..c423184138 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c @@ -0,0 +1,387 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * vdso_tool: a build-time tool for adjusting properties of the "lx_vdso.so.1" + * object we build for VDSO emulation in the LX brand. + * + * This tool ensures that the shared object contains only one loadable program + * header (PT_LOAD), and extends the size of that program header to induce the + * loading of all sections into memory. It also sets a few attributes in the + * ELF header. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> +#include <fcntl.h> +#include <err.h> +#include <errno.h> +#include <libelf.h> +#include <gelf.h> + +#define PROG "vdso_tool" + +typedef enum vdso_flags { + VDSO_UNLINK = 0x0001, + VDSO_UPDATE = 0x0002 +} vdso_flags_t; + +typedef struct vdso { + int v_fd; + char *v_path; + Elf *v_elf; + vdso_flags_t v_flags; + int v_ptload_phdr; + Elf64_Off v_max_offset; +} vdso_t; + +static int +open_vdso(vdso_t **vp, char *path) +{ + vdso_t *v; + + if ((v = calloc(1, sizeof (vdso_t))) == NULL || + (v->v_path = strdup(path)) == NULL) { + err(1, "could not allocate memory"); + } + v->v_ptload_phdr = -1; + v->v_fd = -1; + *vp = v; + + /* + * Open shared object file. + */ + if ((v->v_fd = open(v->v_path, O_RDWR)) == -1) { + (void) fprintf(stderr, "could not open: %s: %s\n", v->v_path, + strerror(errno)); + return (-1); + } + + /* + * Attach libelf. + */ + if ((v->v_elf = elf_begin(v->v_fd, ELF_C_RDWR, NULL)) == NULL) { + (void) fprintf(stderr, "could not attach libelf: %s\n", + elf_errmsg(-1)); + return (-1); + } + + if (elf_kind(v->v_elf) != ELF_K_ELF) { + (void) fprintf(stderr, "wrong elf type\n"); + return (-1); + } + + return (0); +} + +static int +close_vdso(vdso_t *v) +{ + int rval = 0; + + if (v == NULL) { + return (0); + } + + if (v->v_elf != NULL) { + /* + * If we want to write to the file, do so now. + */ + if (v->v_flags & VDSO_UPDATE) { + if (elf_update(v->v_elf, ELF_C_WRITE) == -1) { + (void) fprintf(stderr, "ERROR: elf_update " + "failed: %s\n", elf_errmsg(-1)); + v->v_flags |= VDSO_UNLINK; + rval = -1; + } + } + + /* + * Close the libelf handle for this file. + */ + if (elf_end(v->v_elf) == -1) { + (void) fprintf(stderr, "ERROR: elf_end failed: %s\n", + elf_errmsg(-1)); + v->v_flags |= VDSO_UNLINK; + rval = -1; + } + } + + if (v->v_fd > 0) { + (void) close(v->v_fd); + } + + if (v->v_flags & VDSO_UNLINK) { + (void) fprintf(stderr, "unlinking file: %s\n", v->v_path); + if (unlink(v->v_path) != 0) { + (void) fprintf(stderr, "unlink failed: %s\n", + strerror(errno)); + rval = -1; + } + } + + free(v->v_path); + free(v); + + return (rval); +} + +static int +adjust_elf_ehdr(vdso_t *v) +{ + GElf_Ehdr ehdr; + boolean_t dirty = B_FALSE; + + if (gelf_getehdr(v->v_elf, &ehdr) == NULL) { + (void) fprintf(stderr, "could not get ehdr: %s\n", + elf_errmsg(-1)); + goto errout; + } + + if (ehdr.e_ident[EI_OSABI] != ELFOSABI_NONE) { + (void) fprintf(stdout, "set EI_OSABI = ELFOSABI_NONE\n"); + ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE; + dirty = B_TRUE; + } + + if (ehdr.e_ident[EI_ABIVERSION] != 0) { + (void) fprintf(stdout, "set EI_ABIVERSION = 0\n"); + ehdr.e_ident[EI_ABIVERSION] = 0; + dirty = B_TRUE; + } + + if (dirty && gelf_update_ehdr(v->v_elf, &ehdr) == 0) { + (void) fprintf(stderr, "could not update ehdr: %s\n", + elf_errmsg(-1)); + goto errout; + } + + v->v_flags |= VDSO_UPDATE; + return (0); + +errout: + v->v_flags |= VDSO_UNLINK; + return (-1); +} + +static int +find_pt_load_phdr(vdso_t *v) +{ + size_t nphdr, nloadable = 0; + int i; + + if (elf_getphdrnum(v->v_elf, &nphdr) != 0) { + (void) fprintf(stderr, "could not get phdr count: %s\n", + elf_errmsg(-1)); + goto errout; + } + (void) fprintf(stdout, "phdr count: %d\n", nphdr); + + for (i = 0; i < nphdr; i++) { + GElf_Phdr phdr; + + if (gelf_getphdr(v->v_elf, i, &phdr) == NULL) { + (void) fprintf(stderr, "could not get phdr[%d] count: " + "%s\n", i, elf_errmsg(-1)); + goto errout; + } + + if (phdr.p_type == PT_LOAD) { + if (nloadable++ != 0) { + (void) fprintf(stderr, "multiple PT_LOAD " + "phdrs\n"); + goto errout; + } + + (void) fprintf(stdout, "PT_LOAD header is phdr[%d]\n", + i); + v->v_ptload_phdr = i; + + if (phdr.p_filesz != phdr.p_memsz) { + (void) fprintf(stderr, "mismatched filesz " + "(%llx) and memsz (%llx)\n", phdr.p_filesz, + phdr.p_memsz); + goto errout; + } + + if (phdr.p_filesz == 0) { + (void) fprintf(stderr, "filesz was zero\n"); + goto errout; + } + } + } + + return (0); + +errout: + v->v_flags |= VDSO_UNLINK; + return (-1); +} + +static int +find_maximum_offset(vdso_t *v) +{ + size_t nshdr; + int i; + + if (elf_getshdrnum(v->v_elf, &nshdr) != 0) { + (void) fprintf(stderr, "could not get shdr count: %s\n", + elf_errmsg(-1)); + v->v_flags |= VDSO_UNLINK; + return (-1); + } + (void) fprintf(stdout, "shdr count: %d\n", nshdr); + + for (i = 0; i < nshdr; i++) { + Elf_Scn *scn = elf_getscn(v->v_elf, i); + GElf_Shdr shdr; + Elf64_Off end; + + if (gelf_getshdr(scn, &shdr) == NULL) { + (void) fprintf(stderr, "could not get shdr[%d] " + "count: %s\n", i, elf_errmsg(-1)); + goto errout; + } + + end = shdr.sh_offset + shdr.sh_size; + + if (end > v->v_max_offset) { + v->v_max_offset = end; + } + } + + (void) fprintf(stdout, "maximum offset: %llx\n", v->v_max_offset); + + return (0); + +errout: + v->v_flags |= VDSO_UNLINK; + return (-1); +} + +static int +update_pt_load_size(vdso_t *v) +{ + GElf_Phdr phdr; + + if (gelf_getphdr(v->v_elf, v->v_ptload_phdr, &phdr) == NULL) { + (void) fprintf(stderr, "could not get phdr[%d] count: %s\n", + v->v_ptload_phdr, elf_errmsg(-1)); + goto errout; + } + + (void) fprintf(stdout, "PT_LOAD size is currently %llx\n", + phdr.p_filesz); + if (phdr.p_filesz < v->v_max_offset) { + (void) fprintf(stdout, "extending PT_LOAD size from %llx " + "to %llx\n", phdr.p_filesz, v->v_max_offset); + + phdr.p_memsz = phdr.p_filesz = v->v_max_offset; + + if (gelf_update_phdr(v->v_elf, v->v_ptload_phdr, &phdr) == 0) { + (void) fprintf(stderr, "could not update PT_LOAD " + "phdr: %s", elf_errmsg(-1)); + goto errout; + } + + v->v_flags |= VDSO_UPDATE; + } + + return (0); + +errout: + v->v_flags |= VDSO_UNLINK; + return (-1); +} + +int +main(int argc, char **argv) +{ + vdso_t *v; + char *filen = NULL; + int errflg = 0; + int c; + int status = 0; + boolean_t do_update = B_TRUE; + + while ((c = getopt(argc, argv, ":nf:")) != -1) { + switch (c) { + case 'n': + do_update = B_FALSE; + break; + case 'f': + filen = optarg; + break; + case ':': + (void) fprintf(stderr, "option -%c requires an " + "operand\n", optopt); + errflg++; + break; + case '?': + (void) fprintf(stderr, "unrecognised option: -%c\n", + optopt); + errflg++; + break; + } + } + + if (errflg != 0 || optind != argc || filen == NULL) { + (void) fprintf(stderr, "usage: %s -f <vdso.so>\n", PROG); + return (1); + } + + (void) fprintf(stdout, "vdso file: %s\n", filen); + + if (elf_version(EV_CURRENT) == EV_NONE) { + (void) fprintf(stderr, "libelf mismatch: %s\n", elf_errmsg(-1)); + return (2); + } + + status = 3; + if (open_vdso(&v, filen) == -1) { + goto out; + } + + status++; + if (adjust_elf_ehdr(v) == -1) { + goto out; + } + + status++; + if (find_pt_load_phdr(v) == -1) { + goto out; + } + + status++; + if (find_maximum_offset(v) == -1) { + goto out; + } + + status++; + if (do_update && update_pt_load_size(v) == -1) { + goto out; + } + +out: + status++; + if (close_vdso(v) == 0) { + status = 0; + } + + return (status); +} diff --git a/usr/src/lib/brand/lx/netfiles/Makefile b/usr/src/lib/brand/lx/netfiles/Makefile new file mode 100644 index 0000000000..1d15d69850 --- /dev/null +++ b/usr/src/lib/brand/lx/netfiles/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2015 Joyent, Inc. +# + +TXTS = etc_default_nfs +NFS_DFL = ../../../../cmd/fs.d/nfs/etc/nfs.dfl + +all: $(TXTS) + +include ../Makefile.lx + +lint: + +install: $(ROOTTXTS) + +clean: + -$(RM) etc_default_nfs + +clobber: clean + -$(RM) $(ROOTXMLDOCS) $(ROOTTXTS) + +etc_default_nfs: $(NFS_DFL) + $(RM) $@ + $(CP) $(NFS_DFL) $@ diff --git a/usr/src/lib/brand/lx/testing/Makefile b/usr/src/lib/brand/lx/testing/Makefile new file mode 100644 index 0000000000..9149929b60 --- /dev/null +++ b/usr/src/lib/brand/lx/testing/Makefile @@ -0,0 +1,36 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +TXTS = ltp_skiplist ltp_tests pts_ignorelist + +all: + +include $(SRC)/cmd/Makefile.cmd +include ../Makefile.lx + +all := TARGET= all +install := TARGET= install +clobber := TARGET= clobber + +install: $(ROOTTXTS) + +_msg: + +lint: + +clean: + +clobber: + -$(RM) $(ROOTTXTS) diff --git a/usr/src/lib/brand/lx/testing/Readme_ltp b/usr/src/lib/brand/lx/testing/Readme_ltp new file mode 100644 index 0000000000..5165546c1a --- /dev/null +++ b/usr/src/lib/brand/lx/testing/Readme_ltp @@ -0,0 +1,295 @@ +The Linux Test Project (LTP) provides the basis for testing the lx API +implementation. The project webpage is at http://linux-test-project.github.io/ +and the source is available on GitHub at +https://github.com/linux-test-project/ltp.git. + +LTP should be built and run from within an lx zone. + +To build +-------- +As root first make sure you have the tools installed: + apt-get install build-essential autoconf automake git quota + +For 64-bit: + apt-get install libc6-dev-i386 + +Additional prerequisites are required activate some tests: + apt-get install attr-dev libaio-dev + +As a normal user: + git clone https://github.com/linux-test-project/ltp.git + cd ltp + git checkout ed01f6a05c77f65cb5d1089474c9a0e2129c581a + make autotools + ./configure + make all + +As root: + make install + +The tests can be built in both a zone that has been installed with a 32-bit +version of Linux and another zone that has been installed with a 64-bit version +of the same release. When testing the 64-bit zone, a copy of the 32-bit build +can be run in the 64-bit zone to ensure that 32-bit applications work +correctly on a 64-bit install. + +Running the tests +----------------- +The LTP source tree provides detailed documentation on using the test suite, so +this readme only give a short summary. + +Because many of the tests are targetted at kernel functionality which does not +apply to Illumos, or test capabilities which are not available from within a +zone, or test system call functionality which has not yet been completed, a +skip list is used during the test run to bypass tests which are known to fail. + +The skip list lives in this directory and is delivered on the system. It is +available from within the lx zone as /native/usr/lib/brand/lx/ltp_skiplist. As +new functionality is completed, the skip list should be updated to remove tests +which now work. + +As root: + cd /opt/ltp + /opt/ltp/runltp -f `cat /native/usr/lib/brand/lx/ltp_tests` \ + -S /native/usr/lib/brand/lx/ltp_skiplist -p >/tmp/test.log 2>&1 + +When the test run has finished, the results will be logged in a date/time +stamped file under /opt/ltp/results. The summary at the end of the log file +should show "Total Failures: 0". If not, something is wrong. + +Running tests for development +----------------------------- +The source for the tests can be found under the testcases directory. The +largest and most useful set for lx live under testcases/kernel/syscalls. + +For development purposes, an individual test (or tests) can be run by listing +them in a command file, one per line. For example, with a command file named +~/tcmds, to run the read01 test you setup the file so it looks like this: + read01 read01 + +You can run that specific test as follows: + /opt/ltp/runltp -f ~/tcmds -p -l ~/read.log + +Test status +----------- +This section provides a short summary of the rationale for why tests are being +skipped. + +LTP groups tests into command files (i.e. syscalls, nptl, etc. provided with +the -f option in the runltp command shown above). A complete list of the groups +can be seen in LTP source tree under the runtest directory. Some of these +groups are obviously not applicable when running in an lx zone. The remaining +groups still need work before they can be run. The groups shown in the runltp +command above are expected to work when the skip list is used. The 'syscalls' +command file runs the majority of the actual system call tests which we are +interested in. + +The following table indicates why specific subtests are being skipped. Also +note that the following tests pass in a 64-bit lx zone, but fail in a zone +installed with a 32-bit Linux build: mmap15, open12, openat02 and sendfile09. + + Legend: + x = never in a zone + * = fails on kvm and bare metal too + # = emulation not implemented yet + - = could enable with a test zone config change + +- access06 wants a block device +x acct01 enables bsd process accounting +# add_key01 +# add_key02 +x adjtimex01 +x adjtimex02 +x bdflush01 +x cacheflush01 +x chmod03 need PRIV_SYS_CONFIG to set sticky bit on reg file +- chmod06 needs dev +x chmod07 need PRIV_SYS_CONFIG to set sticky bit on reg file +- chown04 needs dev +- chown04_16 needs dev +# clone02 +# clone08 +- creat06 wants to mount a ro fs +x creat07 we don't behave this way for ETXTBSY +x creat08 sets euid to 'nobody', loses PRIV_FILE_SETID to set sgid +x execve04 we don't behave this way for ETXTBSY +# fallocate01 +# fallocate02 +# fallocate03 +x fchmod02 need PRIV_SYS_CONFIG to set sticky bit on reg file +x fchmod03 need PRIV_SYS_CONFIG to set sticky bit on reg file +- fchmod06 needs dev +# fchown04 mounts +# fchown04_16 +# fcntl06 not supported on linux +# fcntl06_64 +# fcntl23 leases not implemented +# fcntl23_64 " +# fcntl24 " +# fcntl24_64 " +# fcntl25 " +# fcntl25_64 " +# fcntl26 " +# fcntl26_64 " +# fcntl30 +# fcntl30_64 +# fcntl31 setown/getown not impl +# fcntl31_64 +# fcntl32 F_SETLEASE not impl +# fcntl32_64 +# fcntl33 F_SETLEASE not impl +# fcntl33_64 +# fork05 asm into %fs reg +- fork09 needs a swap cap of ~9GB +# fork13 decided not to support this +# fork14 " +# ftruncate04 need a mnt with mandatory locking +# ftruncate04_64 +# getdents02 wrong errno on test 4 - perf. impact too high +# getdents02_64 +# get_mempolicy01 +x getrusage03 we don't fill in the ru_maxrss field +- getxattr01 need attr/xattr.h at build time +- getxattr02 +- getxattr03 +# ioctl03 needs /dev/net/tun +# io_cancel01 libaio stuff not done +# io_destroy01 +# io_getevents01 +# io_setup01 +# io_submit01 +- inotify03 needs dev +# fanotify01 don't have fanotify +# fanotify02 +# fanotify03 +# fanotify04 +# fanotify05 +# keyctl01 no kernel keyring support +- lchown03 needs to mount ro fs +- lchown03_16 +- linkat02 needs dev +- link08 needs dev +x mem01 crashme test which expects OOM killer to save the day +- mkdir03 needs dev +- mkdirat02 needs dev +x mknod01 makes block and chr devs +- mknod07 needs dev +- mknodat02 needs dev +# mmap13 expects "invalid access" to SIGBUS +- mount01 needs dev +- mount02 needs dev +x mount03 mounts ext2 +- mount04 needs dev +x mount05 mounts ext2 +x mount06 mounts ext2 +# mq_notify01 +# mq_notify02 +# mq_open01 +# mq_timedreceive01 +# mq_timedsend01 +# mq_unlink01 +x mremap01 +x mremap02 +x mremap03 +x mremap04 +x mremap05 +# msgctl12 uses MSG_STAT +# msgrcv07 MSG_EXCEPT subtest - not avail. +x open01 need PRIV_SYS_CONFIG to set sticky bit on reg file +# open02 expects NOATIME to cause err for unpriv user +# open10 setgid on sgid subdir behavior +x open11 makes device +# ppoll01 +# process_vm_readv01 +# process_vm_readv02 +# process_vm_readv03 +# process_vm_writev01 +# process_vm_writev02 +# prot_hsymlinks /proc/sys/fs/protected_hardlinks +x ptrace04 not supp on our arch +# ptrace05 OS-3307 +# read02 checks errno for O_DIRECT +# readahead01 +# readahead02 +# readdir21 dir perf. issue +- rename11 needs dev +- renameat01 needs dev +- rmdir02 needs dev +x sched_getparam01 assumes Linux SCHED_OTHER return value +x sched_getparam02 assumes Linux SCHED_OTHER return value +# sched_rr_get_interval01 +# sched_rr_get_interval02 +# sched_rr_get_interval03 +x sched_setparam02 tries to set Linux policies +x sched_setparam03 tries to set Linux policies +# sched_getscheduler01 +# sched_getscheduler02 +x sched_setscheduler01 +x sched_setaffinity01 +x sched_getaffinity01 +# semctl01 all pass but SEM_STAT - linux specific +# semop02 last test fails errno - expensive +# sendfile02 OS-3296 +# sendfile02_64 " +# sendfile04 " +# sendfile04_64 " +# sendfile06 " +# sendfile06_64 " +# sendfile07 " +# sendfile07_64 " +sendmsg01 OS-3295 - tests actually pass +x setfsuid04 no real equiv. and only for NFS server +x setfsuid04_16 +# sgetmask01 obsolete +# setgroups04_16 expects sig11 for certain err +# setns01 +# setns02 +# setpgid02 all pass but one, expects pid 0 to be there +* setregid02 fails on bare metal, expects to lookup group "nobody" which +* setregid02_16 doesn't exist. it is "nogroup" on ubuntu at least +# setrlimit01 all pass but one, expects to set proc limit +x settimeofday01 +# setxattr01 +# setxattr02 +# setxattr03 +# shmget05 OS-3326 +# splice01 +# splice02 +# splice03 +# tee01 +# tee02 +# ssetmask01 obsolete +x stime01 +x switch01 +# sync_file_range01 +# sysconf01 most pass but see OS-3305 +# sysctl01 the build compiled this out, +# sysctl03 this syscall is basically obsolete +# sysctl04 obsolete +# sysctl05 +# syslog01 +# syslog02 +# syslog03 +# syslog04 +# syslog05 +# syslog06 +# syslog07 +# syslog08 +# syslog09 +# syslog10 +# syslog11 +# syslog12 +# unshare01 +# unshare02 +- umount01 needs dev +- umount02 needs dev +- umount03 needs dev +x ustat01 obsolete call to stat FS +x ustat02 obsolete call to stat FS +- utime06 needs dev +- utimes01 needs dev +# utimensat01 many subtests pass but see OS-3328 for the rest +# vmsplice01 +# vmsplice02 +# perf_event_open01 +# perf_event_open02 diff --git a/usr/src/lib/brand/lx/testing/ltp_skiplist b/usr/src/lib/brand/lx/testing/ltp_skiplist new file mode 100644 index 0000000000..09ff2cacc1 --- /dev/null +++ b/usr/src/lib/brand/lx/testing/ltp_skiplist @@ -0,0 +1,272 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# Copyright 2017 Joyent, Inc. + +# Broken tests +poll02 # OS-3997 + +# tests functionality not allowed in a zone +accept4_01 +acct01 +adjtimex01 +adjtimex02 +bdflush01 +cacheflush01 +chmod03 +chmod07 +creat07 +creat08 +execve04 +fchmod02 +fchmod03 +getrusage03 +ioperm01 +ioperm02 +iopl01 +iopl02 +isofs +mbind01 +mem01 +migrate_pages01 +migrate_pages02 +mknod01 +mmap12 +mount03 +mount05 +mount06 +modify_ldt01 +modify_ldt02 +move_pages01 +move_pages02 +move_pages03 +move_pages04 +move_pages05 +move_pages06 +move_pages07 +move_pages08 +move_pages09 +move_pages10 +move_pages11 +mremap04 # we don't yet support remapping shm +open01 +open11 +open12 # needs a device +ptrace04 +quotactl01 +quotactl02 +remap_file_pages01 +remap_file_pages02 +setfsuid04 +setfsuid04_16 +settimeofday01 +stime01 +swapoff02 +swapon02 +swapon03 +switch01 +ustat01 +ustat02 + +# needs a config with a dev or mounting +# As of February 2017, an increasing number of tests require device support +# (often via a zeroed file mounted via loopback) to function properly. +access04 +chmod06 +chown04 +chown04_16 +creat06 +fchmod06 +fchown04 +fchown04_16 +inotify03 +lchown03 +lchown03_16 +linkat02 +link08 +madvise06 +mkdir03 +mkdirat02 +mknod07 +mknodat02 +mmap16 +mount01 +mount02 +mount04 +rename11 +renameat01 +rmdir02 +umount01 +umount02 +umount2_01 +umount2_02 +umount2_03 +umount03 +utime06 +utimes01 + +# These tests are broken on 32-bit (including on "real" Linux) +preadv01 +preadv02 +pwritev01 +pwritev02 + +# tests functionality not implemented yet +access06 +add_key01 +add_key02 +clone02 +clone08 +clone09 +crash02 +dirtyc0w +fallocate04 # needs SEEK_HOLE +fallocate05 # needs to mount a filesystem +fanotify01 +fanotify02 +fanotify03 +fanotify04 +fanotify05 +fanotify06 +fcntl06 +fcntl23 +fcntl23_64 +fcntl24 +fcntl24_64 +fcntl25 +fcntl25_64 +fcntl26 +fcntl26_64 +fcntl31 +fcntl31_64 +fcntl32 +fcntl32_64 +fcntl33 +fcntl33_64 +fork05 +fork09 +fork13 +fork14 +fsync01 +ftruncate04 +ftruncate04_64 +futex_wake04 # OS-4471 +getdents02 +getdents02_64 +get_mempolicy01 +getxattr01 +getxattr02 +getxattr03 +getxattr04 +ioctl03 +ioctl04 +ioctl05 +ioctl06 +ioctl07 # OS-6397 +keyctl01 +keyctl02 +keyctl03 +keyctl04 +keyctl05 +keyctl06 +keyctl07 +kcmp01 +kcmp02 +kcmp03 +lgetxattr01 +lgetxattr02 +llistxattr01 +llistxattr02 +llistxattr03 +madvise08 # no MADV_DONTDUMP support (yet?) +mmap13 +mq_notify01 +mq_notify02 +mq_open01 +mq_timedreceive01 +mq_timedsend01 +mq_unlink01 +msgctl12 +msgrcv07 +msync04 +open02 +open10 +perf_event_open01 +perf_event_open02 +ppoll01 +process_vm_readv01 +process_vm_readv02 +process_vm_readv03 +process_vm_writev01 +process_vm_writev02 +prot_hsymlinks +pty01 # OS-5437 +read02 +readahead01 +readahead02 +readdir2 +readdir21 +removexattr01 +removexattr02 +request_key01 +request_key02 +semctl01 +semop02 +sendfile02 # OS-3296 +sendfile02_64 +sendfile04 +sendfile04_64 +sendfile06 +sendfile06_64 +sendfile07 +sendfile07_64 +sendmsg01 +sendto02 # OS-5764 +setgroups04_16 +setns01 +setns02 +setpgid02 +setregid02 +setregid02_16 +setrlimit01 +setsockopt02 +setxattr01 +setxattr02 +setxattr03 +sgetmask01 +shmget05 # OS-3326 +splice02 # bad test, fails on Linux too +ssetmask01 +sync_file_range01 +sysconf01 # OS-3305 +sysctl01 +sysctl03 +sysctl04 +sysctl05 +syslog01 +syslog02 +syslog03 +syslog04 +syslog05 +syslog06 +syslog07 +syslog08 +syslog09 +syslog10 +syslog11 +syslog12 +tee01 +tee02 +unshare01 +utimensat01 # OS-3328 +unshare02 +vmsplice01 +vmsplice02 diff --git a/usr/src/lib/brand/lx/testing/ltp_tests b/usr/src/lib/brand/lx/testing/ltp_tests new file mode 100644 index 0000000000..86e9725638 --- /dev/null +++ b/usr/src/lib/brand/lx/testing/ltp_tests @@ -0,0 +1 @@ +syscalls,nptl,ipc,pipes,pty,crashme,math diff --git a/usr/src/lib/brand/lx/testing/pts_ignorelist b/usr/src/lib/brand/lx/testing/pts_ignorelist new file mode 100644 index 0000000000..2c4f307b55 --- /dev/null +++ b/usr/src/lib/brand/lx/testing/pts_ignorelist @@ -0,0 +1,366 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# Copyright 2016 Joyent, Inc. + +# The Open POSIX Test Suite (PTS) tests the POSIX conformance of an +# operating system. A test may have one of six different results: +# +# o PASS: the test passed +# o FAIL: the test failed +# o UNRESOLVED: the test was unable to determine pass/fail +# o UNSUPPORTED: this feature is not supported by the OS +# o UNTESTED: this test is not yet implemented +# +# You can read more about these result codes in: +# +# ltp/testcases/open_posix_testsuite/Documentation/HOWTO_ResultCodes +# +# This file (pts_ignorelist) contains a list of tests which are known +# not to pass on lx-brand zones, along with the expected test result. +# These tests are grouped based on the reason for their failure, +# described by the comment above the grouping. Ideally, any test +# resulting in FAILED indicates a bug in lx-brand, but PTS is not +# ideal. Many of the tests will return FAILED when they should return +# UNRESOLVED instead. E.g., some of the MQ tests will return FAILED if +# mq_open() fails while some will return UNRESOLVED. Unless otherwise +# noted in the group comment, assume FAILED is a true bug (as opposed +# to a lack of support by lx-brand). +# +# To run the tests and check your results against this list: +# +# cd ltp/testcases/open_posix_testsuite +# make +# make test 2>&1 | tee pts.log +# diff <(egrep -v '^#|^$' /native/usr/lib/brand/lx/pts_ignorelist | sort) \ +# <(grep execution pts.log | tr -d : | awk '{ print $1, $3 }' | sort) +# +# Any delta reported by the diff is either a regression in lx-brand or +# a change in the test code. + +# +# POSIX message queues are not implemented in lx-brand. In illumos, +# MQs are implemented completely in libc. In Linux, MQs are +# implemented as system calls. The lx-brand code returns ENOSYS for +# all the MQ system calls. The MQ tests are not consistent in how they +# classify a failed mq_open() and thus the tests return a mixture of +# UNRESOLVED and FAILED. +# +conformance/interfaces/fork/fork_19-1 UNRESOLVED +conformance/interfaces/mq_close/mq_close_1-1 UNRESOLVED +conformance/interfaces/mq_close/mq_close_2-1 UNRESOLVED +conformance/interfaces/mq_close/mq_close_3-1 UNRESOLVED +conformance/interfaces/mq_close/mq_close_4-1 UNRESOLVED +conformance/interfaces/mq_getattr/mq_getattr_2-1 UNRESOLVED +conformance/interfaces/mq_getattr/mq_getattr_2-2 UNRESOLVED +conformance/interfaces/mq_getattr/mq_getattr_3-1 UNRESOLVED +conformance/interfaces/mq_getattr/mq_getattr_4-1 UNRESOLVED +conformance/interfaces/mq_notify/mq_notify_1-1 UNRESOLVED +conformance/interfaces/mq_notify/mq_notify_2-1 UNRESOLVED +conformance/interfaces/mq_notify/mq_notify_3-1 UNRESOLVED +conformance/interfaces/mq_notify/mq_notify_4-1 UNRESOLVED +conformance/interfaces/mq_notify/mq_notify_5-1 UNRESOLVED +conformance/interfaces/mq_notify/mq_notify_8-1 FAILED +conformance/interfaces/mq_notify/mq_notify_9-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_1-1 FAILED +conformance/interfaces/mq_open/mq_open_11-1 FAILED +conformance/interfaces/mq_open/mq_open_12-1 FAILED +conformance/interfaces/mq_open/mq_open_13-1 FAILED +conformance/interfaces/mq_open/mq_open_15-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_16-1 FAILED +conformance/interfaces/mq_open/mq_open_18-1 FAILED +conformance/interfaces/mq_open/mq_open_19-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_2-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_20-1 FAILED +conformance/interfaces/mq_open/mq_open_23-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_25-2 FAILED +conformance/interfaces/mq_open/mq_open_27-1 FAILED +conformance/interfaces/mq_open/mq_open_27-2 FAILED +conformance/interfaces/mq_open/mq_open_29-1 FAILED +conformance/interfaces/mq_open/mq_open_7-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_7-2 UNRESOLVED +conformance/interfaces/mq_open/mq_open_7-3 FAILED +conformance/interfaces/mq_open/mq_open_8-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_8-2 UNRESOLVED +conformance/interfaces/mq_open/mq_open_9-1 UNRESOLVED +conformance/interfaces/mq_open/mq_open_9-2 UNRESOLVED +conformance/interfaces/mq_receive/mq_receive_1-1 FAILED +conformance/interfaces/mq_receive/mq_receive_10-1 FAILED +conformance/interfaces/mq_receive/mq_receive_11-1 FAILED +conformance/interfaces/mq_receive/mq_receive_11-2 FAILED +conformance/interfaces/mq_receive/mq_receive_12-1 FAILED +conformance/interfaces/mq_receive/mq_receive_13-1 UNRESOLVED +conformance/interfaces/mq_receive/mq_receive_2-1 UNRESOLVED +conformance/interfaces/mq_receive/mq_receive_5-1 FAILED +conformance/interfaces/mq_receive/mq_receive_7-1 UNRESOLVED +conformance/interfaces/mq_receive/mq_receive_8-1 FAILED +conformance/interfaces/mq_send/mq_send_1-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_10-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_11-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_11-2 UNRESOLVED +conformance/interfaces/mq_send/mq_send_12-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_13-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_14-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_2-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_3-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_3-2 UNRESOLVED +conformance/interfaces/mq_send/mq_send_4-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_4-2 UNRESOLVED +conformance/interfaces/mq_send/mq_send_4-3 UNRESOLVED +conformance/interfaces/mq_send/mq_send_5-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_5-2 UNRESOLVED +conformance/interfaces/mq_send/mq_send_7-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_8-1 UNRESOLVED +conformance/interfaces/mq_send/mq_send_9-1 UNRESOLVED +conformance/interfaces/mq_setattr/mq_setattr_1-1 UNRESOLVED +conformance/interfaces/mq_setattr/mq_setattr_1-2 UNRESOLVED +conformance/interfaces/mq_setattr/mq_setattr_2-1 UNRESOLVED +conformance/interfaces/mq_setattr/mq_setattr_5-1 UNRESOLVED +conformance/interfaces/mq_timedreceive/mq_timedreceive_1-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_10-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_10-2 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_11-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_13-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_14-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_15-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_17-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_17-2 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_17-3 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_18-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_18-2 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_2-1 UNRESOLVED +conformance/interfaces/mq_timedreceive/mq_timedreceive_5-1 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_5-2 FAILED +conformance/interfaces/mq_timedreceive/mq_timedreceive_5-3 UNRESOLVED +conformance/interfaces/mq_timedreceive/mq_timedreceive_7-1 UNRESOLVED +conformance/interfaces/mq_timedreceive/mq_timedreceive_8-1 FAILED +conformance/interfaces/mq_timedsend/mq_timedsend_1-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_10-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_11-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_11-2 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_12-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_13-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_14-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_15-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_16-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_18-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_19-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_2-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_20-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_3-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_3-2 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_4-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_4-2 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_4-3 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_5-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_5-2 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_5-3 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_7-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_8-1 UNRESOLVED +conformance/interfaces/mq_timedsend/mq_timedsend_9-1 UNRESOLVED +conformance/interfaces/mq_unlink/mq_unlink_1-1 UNRESOLVED +conformance/interfaces/mq_unlink/mq_unlink_2-1 UNRESOLVED +conformance/interfaces/mq_unlink/mq_unlink_2-2 UNRESOLVED +conformance/interfaces/mq_unlink/mq_unlink_7-1 FAILED +functional/mqueues/mqueues_send_rev_1 UNRESOLVED +functional/mqueues/mqueues_send_rev_2 UNRESOLVED + +# +# Other features not implemented on Linux. +# +conformance/interfaces/pthread_rwlock_unlock/pthread_rwlock_unlock_4-1 UNSUPPORTED +conformance/interfaces/pthread_rwlock_unlock/pthread_rwlock_unlock_4-2 UNSUPPORTED + +# +# Part of this test is verifying specific thread scheduling and I'm +# not sure how much I trust it. This test fails on native too. +# +conformance/interfaces/sched_setparam/sched_setparam_9-1 FAILED + +# +# Linux doesn't support PTHREAD_SCOPE_PROCESS, see +# pthread_attr_setscope(3). +# +conformance/interfaces/sched_setscheduler/sched_setscheduler_22-1 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_22-2 UNSUPPORTED + +# +# These tests test specific scheduling behavior. They pass on native +# when actually running in the RT class, but we run lx zones in the +# FSS class and can't guarantee specific scheduling order. +# +conformance/interfaces/pthread_create/pthread_create_1-6 FAILED +conformance/interfaces/sem_post/sem_post_8-1 FAILED + +# +# It looks like we are missing some futex support that +# pthread_mutex_timedlock() requires: +# +# futex(0x602820, FUTEX_LOCK_PI_PRIVATE, 1) = -1 ENOSYS +# +functional/threads/pi_test/pi_test_pitest-1 FAILED +functional/threads/pi_test/pi_test_pitest-2 FAILED +functional/threads/pi_test/pi_test_pitest-3 FAILED +functional/threads/pi_test/pi_test_pitest-4 FAILED +functional/threads/pi_test/pi_test_pitest-6 FAILED + +# +# Another case where we need FUTEX_LOCK_PI, but slightly different +# from the ones above. Glibc's implementation of pthread_mutex_lock() +# doesn't check for ENOSYS from FUTEX_LOCK_PI and instead chugs along +# assuming it has the mutex. +# +functional/threads/pi_test/pi_test_pitest-5 UNRESOLVED + +# +# glibc doesn't support PTHREAD_SCOPE_PROCESS. +# +conformance/interfaces/sched_setparam/sched_setparam_20-1 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_21-1 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_21-2 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_15-1 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_15-2 UNSUPPORTED + +# +# _POSIX_SPORADIC_SERVER is not available on Linux. +# +conformance/interfaces/sched_get_priority_max/sched_get_priority_max_1-3 UNSUPPORTED +conformance/interfaces/sched_get_priority_min/sched_get_priority_min_1-3 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_23-2 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_23-3 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_23-4 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_23-5 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_25-2 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_25-3 UNSUPPORTED +conformance/interfaces/sched_setparam/sched_setparam_25-4 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_17-2 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_17-3 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_17-4 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_19-2 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_19-3 UNSUPPORTED +conformance/interfaces/sched_setscheduler/sched_setscheduler_19-4 UNSUPPORTED + +# +# No semaphore limit (_SC_SEM_NSEMS_MAX). +# +conformance/interfaces/sem_init/sem_init_7-1 UNTESTED + +# +# CLOCK_PROCESS_CPUTIME_ID/CLOCK_THREAD_CPUTIME_ID not supported. +# +conformance/interfaces/fork/fork_22-1 UNRESOLVED +conformance/interfaces/timer_create/timer_create_10-1 UNRESOLVED + +# +# Multiple instances of the same signal are coalesced on sigwait(). +# +conformance/interfaces/sigwait/sigwait_2-1 FAILED + +# +# Need sys_time privs. +# +# clock_settime_20-1 returns FAILED because it expects EINVAL but gets EPERM. +# +conformance/interfaces/clock_settime/clock_settime_1-1 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_4-1 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_4-2 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_5-1 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_5-2 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_7-1 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_7-2 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_8-1 UNRESOLVED +conformance/interfaces/clock_settime/clock_settime_20-1 FAILED + +# +# https://github.com/joyent/illumos-joyent/issues/66 +# +conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_1-1 FAILED +conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_1-2 UNRESOLVED +conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_2-1 FAILED +conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_5-1 UNRESOLVED + +# +# Various AIO functions don't EINVAL on bogus struct aiocb. +# +# Faulty on lx brand, Linux on KVM, and illumos. +# +conformance/interfaces/aio_error/aio_error_3-1 UNTESTED +conformance/interfaces/aio_return/aio_return_4-1 UNTESTED + +# +# _SC_AIO_MAX == -1 +# +conformance/interfaces/aio_read/aio_read_9-1 UNSUPPORTED +conformance/interfaces/aio_write/aio_write_7-1 UNSUPPORTED + +# +# PTS is a wasteland, these tests hard-coded to fail. +# +conformance/interfaces/aio_suspend/aio_suspend_5-1 UNSUPPORTED + +# +# Tests that fail on lx-brand and Linux but pass illumos. +# +conformance/interfaces/aio_return/aio_return_2-1 UNTESTED +conformance/interfaces/aio_return/aio_return_3-2 UNTESTED +conformance/interfaces/pthread_rwlock_rdlock/pthread_rwlock_rdlock_2-1 FAILED +conformance/interfaces/pthread_rwlock_rdlock/pthread_rwlock_rdlock_2-2 FAILED + +# +# Tests that fail everywhere: lx-brand, Linux, and illumos. +# +conformance/interfaces/pthread_rwlock_unlock/pthread_rwlock_unlock_3-1 FAILED +conformance/interfaces/sched_getparam/sched_getparam_6-1 UNTESTED +conformance/interfaces/sched_getscheduler/sched_getscheduler_7-1 UNTESTED + +# +# mmap() is supposed to return ENODEV for pipe but returns ENOSYS. +# This is an illumos bug. +# +conformance/interfaces/mmap/mmap_23-1 FAILED + +# +# I'm not sure. +# +conformance/interfaces/mmap/mmap_18-1 FAILED + +# +# According to the spec "Memory access within the mapping but beyond +# the current end of the underlying objects __may__ result in SIGBUS +# signals being sent to the process". So I don't think it breaks POSIX +# but it surely doesn't behave like Linux. +# +conformance/interfaces/mmap/mmap_11-2 FAILED + +# +# The test uses /proc/mounts to check for noatime but the lx-brand +# does not indicate noatime even if the underlying zfs fs has atime +# disabled. +# +conformance/interfaces/mmap/mmap_13-1 FAILED + +# +# Doesn't run on 64bit. +# +conformance/interfaces/mmap/mmap_31-1 UNSUPPORTED + +# +# Bytes written past the end of the mmap object (but still in page) +# are not written to disk but they do appear to persist in the page +# when mmap'd by another pid. Perhaps this test is really testing an +# implementation detail of Linux's VM system. +# +conformance/interfaces/mmap/mmap_11-4 FAILED + +# +# lx-brand doesn't implement clock_getcpudclockid(pid,...). +# +conformance/interfaces/pthread_condattr_setclock/pthread_condattr_setclock_1-3 FAILED diff --git a/usr/src/lib/brand/lx/zone/Makefile b/usr/src/lib/brand/lx/zone/Makefile new file mode 100644 index 0000000000..f5ce1579c7 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/Makefile @@ -0,0 +1,72 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2020 Joyent, Inc. +# Copyright 2017 ASS-Einrichtungssysteme GmbH, Inc. All rights reserved. +# + +PROGS = lx_boot lx_install lx_uninstall prestate poststate statechange +PROGS += lx_boot_zone_redhat lx_boot_zone_ubuntu lx_boot_zone_debian +PROGS += lx_boot_zone_busybox lx_boot_zone_suse lx_boot_zone_docker +PROGS += lx_boot_zone_void +XMLDOCS = config.xml platform.xml +TEMPLATES = SUNWlx.xml SUNWlx26.xml + +all: $(PROGS) + +include $(SRC)/cmd/Makefile.cmd +include ../Makefile.lx + +all := TARGET= all +install := TARGET= install +clobber := TARGET= clobber + +POFILES= $(PROGS:%=%.po) +POFILE= lx_zone.po + +$(POFILE): $(POFILES) + $(RM) $@ + $(BUILDPO.pofiles) + +_msg: $(MSGDOMAINPOFILE) + +install: $(PROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES) $(ROOTPROGS) + mkdir -p $(ROOT)/usr/lib/brand/lx/ld/64 + crle -c $(ROOT)/usr/lib/brand/lx/ld/ld.config \ + -l /native/lib:/native/usr/lib \ + -s /native/lib/secure:/native/usr/lib/secure + crle -64 -c $(ROOT)/usr/lib/brand/lx/ld/64/ld.config \ + -l /native/lib/64:/native/usr/lib/64 \ + -s /native/lib/secure/64:/native/usr/lib/secure/64 + +lint: + +clean: + -$(RM) $(PROGS) + +clobber: clean + -$(RM) $(ROOTXMLDOCS) $(ROOTPROGS) $(ROOTTEMPLATES) + +FRC: + +include $(SRC)/Makefile.msg.targ diff --git a/usr/src/lib/brand/lx/zone/SUNWlx.xml b/usr/src/lib/brand/lx/zone/SUNWlx.xml new file mode 100644 index 0000000000..04c38873de --- /dev/null +++ b/usr/src/lib/brand/lx/zone/SUNWlx.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + ident "%Z%%M% %I% %E% SMI" + + DO NOT EDIT THIS FILE. Use zonecfg(1M) instead. +--> + +<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1"> + +<zone name="default" zonepath="" autoboot="false" brand="lx"> +</zone> diff --git a/usr/src/lib/brand/lx/zone/SUNWlx26.xml b/usr/src/lib/brand/lx/zone/SUNWlx26.xml new file mode 100644 index 0000000000..9bd8af4d92 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/SUNWlx26.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + ident "%Z%%M% %I% %E% SMI" + + DO NOT EDIT THIS FILE. Use zonecfg(1M) instead. +--> + +<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1"> + +<zone name="default" zonepath="" autoboot="false" brand="lx"> + <attr name="kernel-version" type="string" value="2.6"/> +</zone> diff --git a/usr/src/lib/brand/lx/zone/config.xml b/usr/src/lib/brand/lx/zone/config.xml new file mode 100644 index 0000000000..ba2192f867 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/config.xml @@ -0,0 +1,111 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + Copyright 2015, Joyent, Inc. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE brand PUBLIC "-//Sun Microsystems Inc//DTD Brands//EN" + "file:///usr/share/lib/xml/dtd/brand.dtd.1"> + +<brand name="lx"> + <modname>lx_brand</modname> + + <initname>/native/usr/lib/brand/lx/lxinit</initname> + <login_cmd>/bin/login -h zone:%Z %u</login_cmd> + <forcedlogin_cmd>/bin/login -h zone:%Z -f %u</forcedlogin_cmd> + <user_cmd>/usr/bin/getent passwd %u</user_cmd> + + <install>/usr/lib/brand/lx/lx_install -z %z -R %R</install> + <installopts>R:t:U:q:z:</installopts> + <boot>/usr/lib/brand/lx/lx_boot %z %R</boot> + <halt>/usr/lib/brand/lx/lx_support halt %R %z</halt> + <verify_cfg>/usr/lib/brand/lx/lx_support verify</verify_cfg> + <verify_adm></verify_adm> + <postclone></postclone> + <postinstall></postinstall> + <uninstall>/usr/lib/brand/lx/lx_uninstall -z %z -R %R</uninstall> + <prestatechange>/usr/lib/brand/lx/prestate %z %R</prestatechange> + <poststatechange>/usr/lib/brand/lx/poststate %z %R</poststatechange> + + <privilege set="default" name="contract_event" /> + <privilege set="default" name="contract_identity" /> + <privilege set="default" name="contract_observer" /> + <privilege set="default" name="dtrace_proc" /> + <privilege set="default" name="dtrace_user" /> + <privilege set="default" name="file_chown" /> + <privilege set="default" name="file_chown_self" /> + <privilege set="default" name="file_dac_execute" /> + <privilege set="default" name="file_dac_read" /> + <privilege set="default" name="file_dac_search" /> + <privilege set="default" name="file_dac_write" /> + <privilege set="default" name="file_owner" /> + <privilege set="default" name="file_setid" /> + <privilege set="default" name="ipc_dac_read" /> + <privilege set="default" name="ipc_dac_write" /> + <privilege set="default" name="ipc_owner" /> + <privilege set="default" name="net_bindmlp" /> + <privilege set="default" name="net_icmpaccess" /> + <privilege set="default" name="net_mac_aware" /> + <privilege set="default" name="net_privaddr" /> + <privilege set="default" name="net_rawaccess" ip-type="exclusive" /> + <privilege set="default" name="proc_chroot" /> + <privilege set="default" name="sys_audit" /> + <privilege set="default" name="proc_audit" /> + <privilege set="default" name="proc_clock_highres" /> + <privilege set="default" name="proc_lock_memory" /> + <privilege set="default" name="proc_owner" /> + <privilege set="default" name="proc_secflags" /> + <privilege set="default" name="proc_setid" /> + <privilege set="default" name="proc_prioup" /> + <privilege set="default" name="proc_taskid" /> + <privilege set="default" name="sys_acct" /> + <privilege set="default" name="sys_admin" /> + <privilege set="default" name="sys_fs_import" /> + <privilege set="default" name="sys_ip_config" ip-type="exclusive" /> + <privilege set="default" name="sys_iptun_config" ip-type="exclusive" /> + <privilege set="default" name="sys_mount" /> + <privilege set="default" name="sys_nfs" /> + <privilege set="default" name="sys_smb" /> + <privilege set="default" name="sys_resource" /> + <privilege set="default" name="sys_ppp_config" ip-type="exclusive" /> + + <privilege set="prohibited" name="dtrace_kernel" /> + <privilege set="prohibited" name="proc_zone" /> + <privilege set="prohibited" name="sys_config" /> + <privilege set="prohibited" name="sys_devices" /> + <privilege set="prohibited" name="sys_ip_config" ip-type="shared" /> + <privilege set="prohibited" name="sys_linkdir" /> + <privilege set="prohibited" name="sys_net_config" /> + <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" /> + <privilege set="prohibited" name="sys_res_config" /> + <privilege set="prohibited" name="sys_suser_compat" /> + <privilege set="prohibited" name="xvm_control" /> + <privilege set="prohibited" name="virt_manage" /> + + <privilege set="required" name="proc_exec" /> + <privilege set="required" name="proc_fork" /> + <privilege set="required" name="sys_ip_config" ip-type="exclusive" /> + <privilege set="required" name="sys_mount" /> +</brand> diff --git a/usr/src/lib/brand/lx/zone/lx_boot.ksh b/usr/src/lib/brand/lx/zone/lx_boot.ksh new file mode 100644 index 0000000000..7361e45b52 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot.ksh @@ -0,0 +1,97 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2017 ASS-Einrichtungssysteme GmbH, Inc. +# Copyright 2020 Joyent, Inc. +# +# lx boot script. +# +# The arguments to this script are the zone name and the zonepath. +# + +. /usr/lib/brand/shared/common.ksh + +ZONENAME=$1 +ZONEPATH=$2 +ZONEROOT=$ZONEPATH/root + +w_missing=$(gettext "Warning: \"%s\" is not installed in the global zone") + +arch=`uname -p` +if [ "$arch" = "i386" ]; then + ARCH32=i86 + ARCH64=amd64 +else + echo "Unsupported architecture: $arch" + exit 2 +fi + +# +# Run the lx_support boot hook. +# +/usr/lib/brand/lx/lx_support boot $ZONEPATH $ZONENAME +if (( $? != 0 )) ; then + exit 1 +fi + +BRANDDIR=/native/usr/lib/brand/lx; +EXIT_CODE=1 + +# +# Before we boot we validate and fix, if necessary, the required files within +# the zone. These modifications can be lost if a patch or upgrade is applied +# within the zone, so we validate and fix the zone every time it boots. +# + +# +# Determine the distro. +# +distro="" +if [[ $(zonecfg -z $ZONENAME info attr name=docker) =~ "value: true" ]]; then + distro="docker" +elif [[ -f $ZONEROOT/etc/redhat-release ]]; then + distro="redhat" +elif [[ -f $ZONEROOT/etc/lsb-release ]]; then + if egrep -s Ubuntu $ZONEROOT/etc/lsb-release; then + distro="ubuntu" + elif [[ -f $ZONEROOT/etc/debian_version ]]; then + distro="debian" + fi +elif [[ -f $ZONEROOT/etc/debian_version ]]; then + distro="debian" +elif [[ -f $ZONEROOT/etc/alpine-release ]]; then + distro="busybox" +elif [[ -f $ZONEROOT/etc/SuSE-release ]]; then + distro="suse" +elif [[ -f $ZONEROOT/etc/void-release ]]; then + distro="void" +fi + +[[ -z $distro ]] && fatal "Unsupported distribution!" + +# +# Perform distro-specific customization. +# +. $(dirname $0)/lx_boot_zone_${distro} + +exit 0 diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh new file mode 100644 index 0000000000..1ad83902bc --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh @@ -0,0 +1,168 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# Customisation for busybox-init-based distributions. Assumes to have been +# sourced from lx_boot. +# + +tmpfile=/tmp/lx-busybox.$$ + +# Check that the directories we're writing to aren't symlinks outside the zone +safe_dir /etc +safe_dir /etc/init.d + +# Generate network setup script +# +cat > $tmpfile <<EOF +#!/sbin/runscript +depend() { + need localmount + after bootmisc hwdrivers modules + provide net + keyword nojail noprefix novserver +} +start() { + if [ ! -e /etc/resolv.conf ]; then + echo "# AUTOMATIC ZONE CONFIG" > /etc/resolv.conf +EOF +zonecfg -z $ZONENAME info attr name=resolvers | +awk ' + { + if ($1 == "value:") { + nres = split($2, resolvers, ",") + } + } + END { + for (i = 1; i <= nres; i++) { + printf(" echo \"nameserver %s\" >> %s\n", resolvers[i], + "/etc/resolv.conf") + } + } +' >> $tmpfile +zonecfg -z $ZONENAME info attr name=dns-domain | +awk ' + { + if ($1 == "value:") { + dom = $2 + } + } + END { + printf(" echo \"search %s\" >> %s\n", dom, "/etc/resolv.conf") + } +' >> $tmpfile +cat >> $tmpfile <<EOF + fi + return 0 +} +stop() { + return 0 +} +EOF +fnm=$ZONEROOT/etc/init.d/networking +if [[ -f $fnm || -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 755 $fnm +fi + + +# +# The default /etc/inittab might spawn mingetty on each of the virtual consoles +# as well as xdm on the X console. Since we don't have virtual consoles nor +# an X console, spawn a single mingetty on /dev/console instead. +# +# Don't bother changing the file if it looks like we already did. +# +fnm=$ZONEROOT/etc/inittab +if ! egrep -s "Modified by lx brand" $fnm; then + sed 's/^tty[1-6]:/# Disabled by lx brand: &/' \ + $fnm > $tmpfile + echo "console::respawn:/sbin/getty 38400 console" >> $tmpfile + echo "# Modified by lx brand" >> $tmpfile + + if [[ ! -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 644 $fnm + fi +fi + +# +# The following scripts attempt to start services or otherwise configure the +# system in ways incompatible with zones, so replace them with stubs. +# + +fnm=$ZONEROOT/etc/init.d/fsck +[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm +#!/sbin/runscript + +depend() { + use dev clock modules +} + +start() { + return 0 +} +DONE + +fnm=$ZONEROOT/etc/init.d/hwclock +[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm +#!/sbin/runscript + +depend() { + provide clock +} + +start() { + return 0 +} +DONE + +fnm=$ZONEROOT/etc/init.d/klogd +[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm +#!/sbin/runscript + +depend() { + need clock hostname localmount + before net +} + +start() { + return 0 +} +DONE + +fnm=$ZONEROOT/etc/init.d/sysfs +[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm +#!/sbin/runscript + +depend() { +} + +start() { + return 0 +} +DONE + +# +# Setup for the /dev/shm mount. +# +fnm=$ZONEROOT/etc/fstab +entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm) +if [[ -z "$entry" && ! -h $fnm ]]; then + echo "swapfs /dev/shm tmpfs defaults 0 0" >> $fnm +fi + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh new file mode 100644 index 0000000000..35ae59c19c --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh @@ -0,0 +1,172 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# Customisation for Debian-based distributions. Assumes to have been +# sourced from lx_boot. +# + +tmpfile=/tmp/lx-debian.$$ + + +# Check that the directories we're writing to aren't symlinks outside the zone +safe_dir /etc +safe_dir /etc/init.d +safe_dir /etc/network +safe_dir /etc/rc0.d +safe_dir /etc/rc1.d +safe_dir /etc/rc2.d +safe_dir /etc/rc3.d +safe_dir /etc/rc4.d +safe_dir /etc/rc5.d +safe_dir /etc/rc6.d +safe_dir /etc/rcS.d +safe_opt_dir /etc/selinux + +# Populate resolve.conf setup files +zonecfg -z $ZONENAME info attr name=resolvers | awk ' +BEGIN { + print("# AUTOMATIC ZONE CONFIG") +} +$1 == "value:" { + nres = split($2, resolvers, ","); + for (i = 1; i <= nres; i++) { + print("nameserver", resolvers[i]); + } +} +' > $tmpfile +zonecfg -z $ZONENAME info attr name=dns-domain | awk ' +$1 == "value:" { + dom = $2 +} +END { + print("search", dom); +} +' >> $tmpfile +fnm=$ZONEROOT/etc/resolv.conf +if [[ -f $fnm || -h $fnm ]]; then + mv -f $tmpfile $fnm +fi + +# Override network configuration +zonecfg -z $ZONENAME info net | awk ' +BEGIN { + print("# AUTOMATIC ZONE CONFIG") + print("iface lo inet manual"); +} +$1 == "physical:" { + print("iface", $2, "inet manual"); +} +' > $tmpfile +fnm=$ZONEROOT/etc/network/interfaces +if [[ -f $fnm || -h $fnm ]]; then + mv -f $tmpfile $fnm +fi + +# +# The default /etc/inittab might spawn mingetty on each of the virtual consoles +# as well as xdm on the X console. Since we don't have virtual consoles nor +# an X console, spawn a single mingetty on /dev/console instead. +# +# Don't bother changing the file if it looks like we already did. +# +fnm=$ZONEROOT/etc/inittab +if ! egrep -s "Modified by lx brand" $fnm; then + sed 's/^[1-6]:/# Disabled by lx brand: &/' \ + $fnm > $tmpfile + echo "1:2345:respawn:/sbin/getty 38400 console" >> $tmpfile + echo "# Modified by lx brand" >> $tmpfile + + if [[ ! -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 644 $fnm + fi +fi + +# The Debian init uses a combination of traditional rc-style service +# definitions and upstart-style definitions. + +# +# The following rc-style scripts attempt to start services or otherwise +# configure the system in ways incompatible with zones, so don't execute them +# at boot time. +# +unsupported_rc_services=" + checkfs.sh + checkroot.sh + hwclock.sh + kmod + mtab.sh + procps + udev + udev-mtab +" + +for file in $unsupported_rc_services; do + rm -f $ZONEROOT/etc/init.d/$file + + rc_files="$(echo $ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file)" + + if [[ "$rc_files" != \ + "$ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file" ]]; then + for file in $rc_files; do + rm -f "$file" + done + fi +done + +disable_svc() +{ + fnm=$ZONEROOT/etc/init/$1.override + [[ -h $fnm || -f $fnm ]] && return + echo "manual" > $fnm +} + + +# +# Now customize upstart +# + +RMSVCS=" + network-interface-security + udev + udevmonitor + udevtrigger + udev-fallback-graphics + udev-finish +" +for f in $RMSVCS +do + disable_svc $f +done + +# +# We need to setup for the /dev/shm mount. Unlike some other distros, Debian +# can handle it as either /dev/shm or /run/shm. For simplicity we create an +# fstab entry to force it into the /dev/shm style. +# +fnm=$ZONEROOT/etc/fstab +entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm) +if [[ -z "$entry" && ! -h $fnm ]]; then + echo "swapfs /dev/shm tmpfs defaults 0 0" >> $fnm +fi + +# +# upstart modifications are complete +# +rm -f $tmpfile + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh new file mode 100755 index 0000000000..9d332a723d --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh @@ -0,0 +1,17 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2014 Joyent, Inc. All rights reserved. +# + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh new file mode 100644 index 0000000000..566b401c18 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh @@ -0,0 +1,361 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved. +# + +# +# Since CentOS, Red Hat Enterprise Linux, and Fedora all use approximately +# the same source, this file should be good for all three. +# +# Currently, this file assumed a pre-systemd existence, so this should be +# CentOS 6.x or earlier. Testing has been done on CentOS 6.6. +# + +tmpfile=/tmp/lx-redhat.$$ + + +# Before doing anything else, make sure some Centos-specific dirs are safe. +# /etc/init.d is normally a symlink so we can't easily tell if it's safe so +# check rc.d/init.d instead. + +safe_dir /etc/sysconfig +safe_dir /etc/rc.d +safe_dir /etc/rc.d/init.d +safe_dir /etc/rc.d/rc0.d +safe_dir /etc/rc.d/rc1.d +safe_dir /etc/rc.d/rc2.d +safe_dir /etc/rc.d/rc3.d +safe_dir /etc/rc.d/rc4.d +safe_dir /etc/rc.d/rc5.d +safe_dir /etc/rc.d/rc6.d +safe_opt_dir /etc/selinux + +# Generate the /etc/rc.d/init.d/network rc script +cat > $tmpfile <<EOF +#!/bin/bash +# network Bring up/down networking +# +### BEGIN INIT INFO +# Provides: \$network +# Short-Description: Bring up/down networking +# Description: Bring up/down networking +### END INIT INFO + +case "\$1" in + start) + [ "\$EUID" != "0" ] && exit 4 + + if [ ! -e /etc/resolv.conf ]; then + if [ -h /etc/resolv.conf ]; then + rm -f /etc/resolv.conf + fi + echo "# AUTOMATIC ZONE CONFIG" > /etc/resolv.conf +EOF +zonecfg -z $ZONENAME info attr name=resolvers | +awk ' + { + if ($1 == "value:") { + nres = split($2, resolvers, ",") + } + } + END { + for (i = 1; i <= nres; i++) { + printf(" echo \"nameserver %s\" >> %s\n", resolvers[i], + "/etc/resolv.conf") + } + } +' >> $tmpfile +zonecfg -z $ZONENAME info attr name=dns-domain | +awk ' + { + if ($1 == "value:") { + dom = $2 + } + } + END { + printf(" echo \"search %s\" >> %s\n", dom, "/etc/resolv.conf") + } +' >> $tmpfile +cat >> $tmpfile <<EOF + fi + touch /var/lock/subsys/network + rc=0 + ;; + stop) + [ "\$EUID" != "0" ] && exit 4 + + rm -f /var/lock/subsys/network + rc=0 + ;; + status) + echo "Configured devices:" + echo "lo \$(cd /dev/net; ls)" + echo "Currently active devices:" + echo \$(/sbin/ip -o link show up | awk -F ": " '{ print \$2 }') + rc=0 + ;; + restart|reload|force-reload) + cd "\$CWD" + \$0 stop + \$0 start + rc=\$? + ;; + *) + echo "Usage: \$0 {start|stop|status|restart|reload|force-reload}" + exit 2 +esac + +exit \$rc +EOF +fnm=$ZONEROOT/etc/rc.d/init.d/network +if [[ -f $fnm || -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 755 $fnm +fi + +# This is specific to a systemd-based image +sysdir=$ZONEROOT/etc/systemd/system +if [[ -d $ZONEROOT/etc && ! -h $ZONEROOT/etc && -d $ZONEROOT/etc/systemd && + ! -h $ZONEROOT/etc/systemd && -d $sysdir && ! -h $sysdir ]]; then + # don't use NetworkManager + rm -f $sysdir/dbus-org.freedesktop.nm-dispatcher.service + rm -f $sysdir/multi-user.target.wants/NetworkManager.service + rm -f $sysdir/dbus-org.freedesktop.NetworkManager.service + # our network setup needs to run + fnm=$sysdir/multi-user.target.wants/network.service + if [[ ! -f $fnm ]]; then + ln -s /etc/rc.d/init.d/network \ + $sysdir/multi-user.target.wants/network.service + fi +fi + +# +# The default /etc/inittab only sets the runlevel. Make sure it's runlevel 3 +# and not runlevel 5 (X11). +# Don't bother changing the file if it looks like we already did. +# +fnm=$ZONEROOT/etc/inittab +if ! egrep -s "Modified by lx brand" $fnm; then + sed 's/^id:5:initdefault:/id:3:initdefault: &/' \ + $fnm > $tmpfile + echo "# Modified by lx brand" >> $tmpfile + + if [[ ! -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 644 $fnm + fi +fi + +# +# Ensure svcs depending on $network will start. +# +fnm=$ZONEROOT/etc/sysconfig/network +if ! egrep -s "NETWORKING=yes" $fnm; then + cfghnm=$(zonecfg -z $ZONENAME info attr name=hostname | \ + awk '{if ($1 == "value:") print $2}') + if [[ -z "$cfghnm" ]]; then + cfghnm=$ZONENAME + fi + if [[ ! -h $fnm ]]; then + cat > $fnm <<- EOF + NETWORKING=yes + HOSTNAME=$cfghnm + EOF + fi +fi + +# +# SELinux must be disabled otherwise we won't get past init. +# +fnm=$ZONEROOT/etc/selinux/config +if egrep -s "^SELINUX=enforcing|^SELINUX=permissive" $fnm; then + tmpfile=/tmp/selinux_config.$$ + + sed 's/^SELINUX=.*$/SELINUX=disabled/' $fnm > $tmpfile + if [[ ! -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 644 $fnm + fi +fi + +# +# /etc/rc.d/init.d/keytable tries to load a physical keyboard map, which won't +# work in a zone. If we remove etc/sysconfig/keyboard, it won't try this at all. +# +fnm=$ZONEROOT/etc/sysconfig/keyboard +if [[ ! -h $fnm ]]; then + rm -f $ZONEROOT/etc/sysconfig/keyboard +fi + +# The Centos init uses a combination of traditional rc-style service +# definitions and upstart-style definitions. + +# +# The following rc-style scripts attempt to start services or otherwise +# configure the system in ways incompatible with zones, so don't execute them +# at boot time. +# +unsupported_rc_services=" + acpid + auditd + gpm + hpoj + ip6tables + iptables + irda + irqbalance + iscsi + isdn + kdump + kudzu + mdmpd + mdmonitor + microcode_ctl + netdump + ntpd + ntpdate + pcmcia + psacct + quota_nld + random + rawdevices + smartd +" + +for file in $unsupported_rc_services; do + rm -f $ZONEROOT/etc/rc.d/init.d/$file + + rc_files="$(echo $ZONEROOT/etc/rc.d/rc[0-6].d/[SK]+([0-9])$file)" + + if [[ "$rc_files" != \ + "$ZONEROOT/etc/rc.d/rc[0-6].d/[SK]+([0-9])$file" ]]; then + for file in $rc_files; do + rm -f "$file" + done + fi +done + +disable_svc() +{ + # XXX - TBD does this work like on Ubuntu? + # + fnm=$ZONEROOT/etc/init/$1.override + [[ -h $fnm || -f $fnm ]] && return + echo "manual" > $fnm + + # fnm=$ZONEROOT/etc/init/$1.conf + # rm -f $fnm +} + +RMSVCS="control-alt-delete + ttyS0" + +# +# Now customize upstart services +# + +for f in $RMSVCS +do + disable_svc $f +done + +if [[ ! -f $ZONEROOT/etc/init/tty.override ]]; then + cat > $ZONEROOT/etc/init/tty.override <<- EOF + # tty - getty + # + # This service maintains a getty on the console. + + stop on runlevel [S016] + + respawn + instance console + exec /sbin/mingetty console + EOF +fi + +if [[ ! -f $ZONEROOT/etc/init/start-ttys.override ]]; then + cat > $ZONEROOT/etc/init/start-ttys.override <<- EOF + # This service starts the configured number of gettys. + # + + start on stopped rc RUNLEVEL=[2345] + + task + script + initctl start tty + end script + EOF +fi + +# +# There is a lot of stuff in the standard halt and reboot scripts that we +# have no business running in a zone. Fortunately, the stuff we want to +# skip is all in one contiguous chunk. +# +# Don't bother to modify the file if it looks like we already did. +# +fnm=$ZONEROOT/etc/rc.d/init.d/halt +if ! egrep -s "Disabled by lx brand" $fnm; then + awk 'BEGIN {skip = ""} + /^# Save mixer/ {skip = "# Disabled by lx brand: "} + /halt.local/ {skip = ""} + /./ {print skip $0}' $fnm > /tmp/halt.$$ + + if [[ $? -eq 0 && ! -h $fnm ]]; then + mv -f /tmp/halt.$$ $fnm + chmod 755 $fnm + fi +fi + +# +# Fix up /etc/rc.d/rc.sysinit: +# +# 1) /sbin/hwclock requires the iopl() system call, which BrandZ won't support. +# Since the hardware clock cannot be set from within a zone, we comment out +# the line. +# +# 2) Disable dmesg commands, since we don't implement klogctl +# +# 3) Disable initlog and the mount of /dev/pts +# +# 4) Don't touch /dev/tty* in order to start virtual terminals, as that won't +# work from within a zone. +# +# 5) Don't try to check the root filesystem (/) as there is no associated +# physical device, and any attempt to run fsck will fail. +# +fnm=$ZONEROOT/etc/rc.d/rc.sysinit +tmpfile=/tmp/lx_rc.sysinit.$$ + +sed 's@^/sbin/hwclock@# lx: &@ + s@^/bin/dmesg -n@# lx: &@ + s@^dmesg -s@# lx: &@ + s@^initlog -c \"fsck@# lx: &@ + s@^mount -n -o remount /dev/shm @mount -t tmpfs tmpfs /dev/shm @ + s@^mount .* /dev/pts@# lx: &@ + /^#remount \/dev\/shm/d' \ + $fnm > $tmpfile + +if [[ ! -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 755 $fnm +fi + +# +# sysinit modifications are complete +# +rm -f $tmpfile + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh new file mode 100644 index 0000000000..dd8b4e3721 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh @@ -0,0 +1,213 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2017 Joyent, Inc. +# Copyright 2017 ASS-Einrichtungssysteme GmbH, Inc. +# + +# +# Customisation for SuSE-based distributions. Assumes to have been +# sourced from lx_boot. +# + +tmpfile=/tmp/lx-suse.$$ + + +# Check that the directories we're writing to aren't symlinks outside the zone +safe_dir /etc +safe_dir /etc/init.d +safe_dir /etc/rc.d/rc0.d +safe_dir /etc/rc.d/rc1.d +safe_dir /etc/rc.d/rc2.d +safe_dir /etc/rc.d/rc3.d +safe_dir /etc/rc.d/rc4.d +safe_dir /etc/rc.d/rc5.d +safe_dir /etc/rc.d/rc6.d +safe_dir /etc/rc.d/rcS.d +safe_dir /etc/sysconfig +safe_dir /etc/sysconfig/network +safe_opt_dir /etc/systemd +safe_opt_dir /etc/systemd/system +safe_opt_dir /etc/systemd/system/multi-user.target.wants +safe_opt_dir /etc/systemd/system/network-online.target.wants +safe_dir /etc/YaST2 +safe_opt_dir /etc/selinux + +# Populate resolve.conf setup files +zonecfg -z $ZONENAME info attr name=resolvers | awk ' +BEGIN { + print("# AUTOMATIC ZONE CONFIG") +} +$1 == "value:" { + nres = split($2, resolvers, ","); + for (i = 1; i <= nres; i++) { + print("nameserver", resolvers[i]); + } +} +' > $tmpfile +zonecfg -z $ZONENAME info attr name=dns-domain | awk ' +$1 == "value:" { + dom = $2 +} +END { + print("search", dom); +} +' >> $tmpfile +fnm=$ZONEROOT/etc/resolv.conf +if [[ -f $fnm || -h $fnm ]]; then + mv -f $tmpfile $fnm +fi + +# network configuration +netdir="$ZONEROOT/etc/sysconfig/network" + +# first cleanup potentially obsolete configuration +rm -f $netdir/ifcfg-* + +# Override network configuration for Loopback (lo) configuration +cat <<LOEOF > $netdir/ifcfg-lo +# AUTOMATIC ZONE CONFIG +IPADDR=127.0.0.1/8 +NETMASK=255.0.0.0 +NETWORK=127.0.0.0 +STARTMODE=nfsroot +BOOTPROTO=static +USERCONTROL=no +FIREWALL=no +LOEOF + +zonecfg -z $ZONENAME info net | awk -v npath=$netdir ' +$1 == "physical:" { + fname = npath "/ifcfg-" $2 + print("# Automatic zone config for interface:", $2) > fname + print("STARTMODE=auto") >> fname + print("BOOTPROTO=dhcp4") >> fname +} +$1 == "property:" && $2 == "(name=primary,value=\"true\")" { + print("DHCLIENT_SET_DEFAULT_ROUTE=yes") >> fname +}' + +# This is specific to a systemd-based image +sysdir="$ZONEROOT/etc/systemd/system" +if [[ -d $sysdir ]]; then + # don't use NetworkManager wickedd service units + rm -f $sysdir/dbus-org.opensuse.Network.AUTO4.service + rm -f $sysdir/dbus-org.opensuse.Network.DHCP4.service + rm -f $sysdir/dbus-org.opensuse.Network.DHCP6.service + rm -f $sysdir/dbus-org.opensuse.Network.Nanny.service + rm -f $sysdir/network-online.target.wants/wicked.service + rm -f $sysdir/multi-user.target.wants/wicked.service + # our network setup needs to run + fnm=$sysdir/multi-user.target.wants/network.service + if [[ ! -f $fnm ]]; then + ln -s /usr/lib/systemd/system/wicked.service \ + $sysdir/network.service + fi + # disable purge-kernels.service + rm -f $sysdir/multi-user.target.wants/purge-kernels.service +fi + +# +# The default /etc/inittab might spawn mingetty on each of the virtual consoles +# as well as xdm on the X console. Since we don't have virtual consoles nor +# an X console, spawn a single mingetty on /dev/console instead. +# +# Don't bother changing the file if it looks like we already did. +# +fnm=$ZONEROOT/etc/inittab +if ! egrep -s "Modified by lx brand" $fnm; then + sed 's/^[1-6]:/# Disabled by lx brand: &/' \ + $fnm > $tmpfile + echo "1:2345:respawn:/sbin/getty 38400 console" >> $tmpfile + echo "# Modified by lx brand" >> $tmpfile + + if [[ ! -h $fnm ]]; then + mv -f $tmpfile $fnm + chmod 644 $fnm + fi +fi + +# The SuSE init uses a combination of traditional rc-style service +# definitions and systemd-style definitions. + +# +# The following rc-style scripts attempt to start services or otherwise +# configure the system in ways incompatible with zones, so don't execute them +# at boot time. +# +unsupported_rc_services=" + checkfs.sh + checkroot.sh + hwclock.sh + kmod + mtab.sh + procps + udev + udev-mtab +" + +for file in $unsupported_rc_services; do + rm -f $ZONEROOT/etc/init.d/$file + + rc_files="$(echo $ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file)" + + if [[ "$rc_files" != \ + "$ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file" ]]; then + for file in $rc_files; do + rm -f "$file" + done + fi +done + +disable_svc() +{ + fnm=$ZONEROOT/etc/init/$1.override + [[ -h $fnm || -f $fnm ]] && return + echo "manual" > $fnm +} + + +# +# Now customize systemd +# + +RMSVCS=" + network-interface-security + udev + udevmonitor + udevtrigger + udev-fallback-graphics + udev-finish +" +for f in $RMSVCS +do + disable_svc $f +done + +# +# We need to setup for the /dev/shm mount. Unlike some other distros, SuSE +# can handle it as either /dev/shm or /run/shm. For simplicity we create an +# fstab entry to force it into the /dev/shm style. +# +fnm=$ZONEROOT/etc/fstab +entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm) +if [[ -z "$entry" && ! -h $fnm ]]; then + echo "swapfs /dev/shm tmpfs defaults 0 0" >> $fnm +fi + +# +# systemd modifications are complete +# +rm -f $tmpfile + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh new file mode 100644 index 0000000000..27d047e4ca --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh @@ -0,0 +1,139 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +# +# Customisation for Ubuntu-based distributions. Assumes to have been +# sourced from lx_boot. +# +tmpfile=/tmp/lx-ubuntu.$$ + +# Check that the directories we're writing to aren't symlinks outside the zone +safe_dir /etc +safe_dir /etc/init +safe_dir /etc/resolvconf +safe_dir /etc/resolvconf/resolv.conf.d +safe_dir /etc/network +safe_dir /etc/network/interfaces.d +safe_dir /etc/network/interfaces.d/smartos + +# Populate resolve.conf setup files +zonecfg -z $ZONENAME info attr name=resolvers | awk ' +BEGIN { + print("# AUTOMATIC ZONE CONFIG") +} +$1 == "value:" { + nres = split($2, resolvers, ","); + for (i = 1; i <= nres; i++) { + print("nameserver", resolvers[i]); + } +} +' > $tmpfile +zonecfg -z $ZONENAME info attr name=dns-domain | awk ' +$1 == "value:" { + dom = $2 +} +END { + print("search", dom); +} +' >> $tmpfile +fnm=$ZONEROOT/etc/resolvconf/resolv.conf.d/tail +if [[ -f $fnm || -h $fnm || ! -e $fnm ]]; then + mv -f $tmpfile $fnm +fi + +# Override network configuration +zonecfg -z $ZONENAME info net | awk ' +BEGIN { + print("# AUTOMATIC ZONE CONFIG") +} +$1 == "physical:" { + print("iface", $2, "inet manual"); +} +' > $tmpfile +fnm=$ZONEROOT/etc/network/interfaces.d/smartos +if [[ -f $fnm || -h $fnm ]]; then + mv -f $tmpfile $fnm +fi + +src_fnm=$ZONEROOT/etc/init/console.conf +tgt_fnm=$ZONEROOT/etc/init/console.override +if [[ -f $src_fnm && ! -f $tgt_fnm && ! -h $tgt_fnm ]] then + sed -e 's/lxc/smartos/' $src_fnm > /tmp/console.conf.$$ + mv /tmp/console.conf.$$ $tgt_fnm +fi + +fnm=$ZONEROOT/etc/init/container-detect.override +if [[ ! -f $fnm && ! -h $fnm ]] then + cat <<'DONE' > $fnm +description "Track if upstart is running in a container" + +start on mounted MOUNTPOINT=/run + +env container +env LIBVIRT_LXC_UUID + +emits container + +pre-start script + container=smartos + echo "$container" > /run/container_type || true + initctl emit --no-wait container CONTAINER=$container + exit 0 +end script +DONE +fi + +# XXX need to add real mounting into this svc definition + +fnm=$ZONEROOT/etc/init/mountall.override +if [[ ! -h $fnm ]] then + cat <<DONE > $fnm +description "Mount filesystems on boot" + +start on startup + +task + +emits virtual-filesystems +emits local-filesystems +emits remote-filesystems +emits all-swaps +emits filesystem +emits mounted + +script + echo "/dev/zfsds0 / zfs rw 0 0" > /etc/mtab + echo "proc /proc proc rw,noexec,nosuid,nodev 0 0" >> /etc/mtab + + /sbin/initctl emit --no-wait virtual-filesystems + /bin/mount -t tmpfs tmpfs /dev/shm || true + /bin/mount -t tmpfs tmpfs /run || true + /bin/mkdir -p /run/lock || true + /bin/ln -s /dev/shm /run/shm || true + /sbin/initctl emit --no-wait mounted MOUNTPOINT=/run TYPE=tmpfs + /sbin/initctl emit --no-wait local-filesystems + /sbin/initctl emit --no-wait all-swaps + /sbin/initctl emit --no-wait filesystem +end script +DONE +fi + +# +# upstart modifications are complete +# +rm -f $tmpfile + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh new file mode 100755 index 0000000000..df7218ca21 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh @@ -0,0 +1,17 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +# Hand control back to lx_boot diff --git a/usr/src/lib/brand/lx/zone/lx_install.ksh b/usr/src/lib/brand/lx/zone/lx_install.ksh new file mode 100644 index 0000000000..d979bb373e --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_install.ksh @@ -0,0 +1,62 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# Does this brand support reprovisioning? +jst_reprovision="yes" + +# Is a template image optional? +# jst_tmplopt="yes" + +. /usr/lib/brand/jcommon/libhooks.ksh + +function jcommon_attach_hook +{ + # lx-brand specific stuff is done here as final step of install + + # + # Make sure the localhost has a hostname alias in the zone's + # /etc/hosts file + # + zroot=$ZONEPATH/root + hname=$ZONENAME + hostnamef=$zroot/etc/hostname + if [[ ! -h $hostnamef ]]; then + echo $hname >$hostnamef + fi + + hostfile=$zroot/etc/hosts + if [[ -f $hostfile && ! -h $hostfile ]]; then + # use awk to search and append to loopback in one command + awk -v hname="$hname" '{ + if ($1 ~ /^127\./ && index($0, hname) == 0) { + printf("%s %s\n", $0, hname); + } else { + print $0 + } + }' $hostfile >/tmp/tmp_${ZONENAME}_$$ + mv /tmp/tmp_${ZONENAME}_$$ $hostfile + chmod 644 $hostfile + fi + + rm -rf $ZONEPATH/cores + CORE_QUOTA=102400 + zfs create -o quota=${CORE_QUOTA}m \ + -o mountpoint=/${PDS_NAME}/$bname/cores ${PDS_NAME}/cores/$bname + + chmod 700 $ZONEPATH +} + +. /usr/lib/brand/jcommon/cinstall diff --git a/usr/src/lib/brand/lx/zone/lx_uninstall.ksh b/usr/src/lib/brand/lx/zone/lx_uninstall.ksh new file mode 100755 index 0000000000..136044ad9e --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_uninstall.ksh @@ -0,0 +1,23 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +function jcommon_uninstall_hook +{ + : +} + +. /usr/lib/brand/jcommon/cuninstall diff --git a/usr/src/lib/brand/lx/zone/platform.xml b/usr/src/lib/brand/lx/zone/platform.xml new file mode 100644 index 0000000000..060343e38f --- /dev/null +++ b/usr/src/lib/brand/lx/zone/platform.xml @@ -0,0 +1,163 @@ +<?xml version="1.0"?> + +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2007 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + Copyright 2017 Joyent, Inc. + + DO NOT EDIT THIS FILE. +--> + +<!DOCTYPE platform PUBLIC "-//Sun Microsystems Inc//Zones Platform//EN" + "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1"> + +<platform name="lx" allow-exclusive-ip="true"> + <!-- Global filesystems to mount when booting the zone --> + <global_mount special="/dev" directory="/native/dev" type="dev" + opt="attrdir=%R/dev" /> + + <!-- + Local filesystems to mount when booting the zone. The /native/sbin + entry is needed so that we can replace ifconfig with its native + counterpart (this is required for exclusive-stack zones to work). + We also need dladm from /native/sbin. + --> + <global_mount special="/lib" directory="/native/lib" + opt="ro" type="lofs" /> + <global_mount special="/usr" directory="/native/usr" + opt="ro" type="lofs" /> + <global_mount special="/usr/lib/brand/lx/etc_default_nfs" + directory="/native/etc/default/nfs" type="lofs" opt="ro" /> + <global_mount special="/etc/default/dhcpagent" + directory="/native/etc/default/dhcpagent" type="lofs" opt="ro" /> + <global_mount special="/etc/netconfig" + directory="/native/etc/netconfig" type="lofs" opt="ro" /> + <global_mount special="/etc/nfssec.conf" + directory="/native/etc/nfssec.conf" type="lofs" opt="ro" /> + <global_mount special="/sbin" + directory="/native/sbin" opt="ro,nodevices" type="lofs" /> + <global_mount special="/usr/lib/brand/lx/ld" directory="/var/ld" + opt="ro" type="lofs" /> + <global_mount special="/etc/zones/%z.xml" + directory="/native/etc/zones/%z.xml" opt="ro" type="lofs" /> + <global_mount special="/var/zonecontrol/%z" directory="/native/.zonecontrol" + opt="ro,nodevices,nosetuid,noexec" type="lofs" /> + + <!-- Local filesystems to mount when booting the zone --> + <mount special="/native/dev" directory="/dev" type="lx_devfs" /> + <mount special="proc" directory="/native/proc" type="proc" /> + <mount special="swap" directory="/native/etc/svc/volatile" + type="tmpfs" /> + <mount special="swap" directory="/native/tmp" type="tmpfs" /> + <mount special="objfs" directory="/system/object" type="objfs" /> + <mount special="ctfs" directory="/system/contract" type="ctfs" /> + <mount special="mnttab" directory="/etc/mnttab" type="mntfs" /> + <mount special="lxproc" directory="/proc" type="lx_proc" /> + + <!-- Devices to create under /dev --> + <device match="arp" /> + <device match="dtrace/*" /> + <device match="dtrace/provider/*" /> + <device match="eventfd" /> + <device match="full" /> + <device match="inotify" /> + <device match="ipnet" /> + <device match="kstat" /> + <device match="lo0" /> + <device match="null" /> + <device match="poll" /> + <device match="pts/*" /> + <device match="random" /> + <device match="signalfd" /> + <device match="tcp" /> + <device match="tcp6" /> + <device match="ticotsord" /> + <device match="timerfd" /> + <device match="tty" /> + <device match="udp" /> + <device match="udp6" /> + <device match="urandom" /> + <device match="zero" /> + <device match="zfs" /> + <device match="zvol/dsk/%P/%z/*" /> + <device match="zvol/rdsk/%P/%z/*" /> + + <!-- Devices to create in exclusive IP zone only --> + <device match="dld" ip-type="exclusive" /> + <device match="icmp" ip-type="exclusive" /> + <device match="icmp6" ip-type="exclusive" /> + <device match="ip" ip-type="exclusive" /> + <device match="ip6" ip-type="exclusive" /> + <device match="ipauth" ip-type="exclusive" /> + <device match="ipf" ip-type="exclusive" /> + <device match="ipl" ip-type="exclusive" /> + <device match="iplookup" ip-type="exclusive" /> + <device match="ipmpstub" ip-type="exclusive" /> + <device match="ipnat" ip-type="exclusive" /> + <device match="ipscan" ip-type="exclusive" /> + <device match="ipsecah" ip-type="exclusive" /> + <device match="ipsecesp" ip-type="exclusive" /> + <device match="ipstate" ip-type="exclusive" /> + <device match="ipsync" ip-type="exclusive" /> + <device match="keysock" ip-type="exclusive" /> + <device match="net/*" ip-type="exclusive" /> + <device match="rawip" ip-type="exclusive" /> + <device match="rawip6" ip-type="exclusive" /> + <device match="rts" ip-type="exclusive" /> + <device match="sad/admin" ip-type="exclusive" /> + <device match="sctp" ip-type="exclusive" /> + <device match="sctp6" ip-type="exclusive" /> + <device match="spdsock" ip-type="exclusive" /> + <device match="sppp" ip-type="exclusive" /> + <device match="sppptun" ip-type="exclusive" /> + <device match="vni" ip-type="exclusive" /> + + <!-- Renamed devices to create under /dev --> + <device match="brand/lx/autofs" name="autofs" /> + <device match="brand/lx/ptmx" name="ptmx" /> + <device match="zcons/%z/zoneconsole" name="console" /> + <device match="zfd/%z/slave/0" name="zfd/0" /> + <device match="zfd/%z/slave/1" name="zfd/1" /> + <device match="zfd/%z/slave/2" name="zfd/2" /> + <device match="zfd/%z/slave/3" name="zfd/3" /> + <device match="zfd/%z/slave/4" name="zfd/4" /> + + <!-- Audio devices to create under /dev --> + <device match="brand/lx/dsp" name="dsp" /> + <device match="brand/lx/mixer" name="mixer" /> + + <!-- Symlinks to create under /dev --> + <symlink source="fd" target="../proc/self/fd" /> + <symlink source="stderr" target="../proc/self/fd/2" /> + <symlink source="stdin" target="../proc/self/fd/0" /> + <symlink source="stdout" target="../proc/self/fd/1" /> + <symlink source="systty" target="console" /> + <symlink source="kmsg" target="console" /> + <symlink source="conslog" target="console" /> + + <!-- Create a mount point for /dev/shm tmpfs (see shm_overview(7)) --> + <!-- We need to force a dir for the Linux mount to work ok --> + <symlink source="shm/loop" target="." /> + <!-- Create a dummy /dev/xconsole --> + <device match="null" name="xconsole" /> + +</platform> diff --git a/usr/src/lib/brand/lx/zone/poststate.ksh b/usr/src/lib/brand/lx/zone/poststate.ksh new file mode 100755 index 0000000000..49370fce60 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/poststate.ksh @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="lx" +. /usr/lib/brand/jcommon/poststate diff --git a/usr/src/lib/brand/lx/zone/prestate.ksh b/usr/src/lib/brand/lx/zone/prestate.ksh new file mode 100755 index 0000000000..04d9969a65 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/prestate.ksh @@ -0,0 +1,19 @@ +#!/bin/ksh -p +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +ps_brand="lx" +. /usr/lib/brand/jcommon/prestate diff --git a/usr/src/lib/brand/lx/zone/statechange.ksh b/usr/src/lib/brand/lx/zone/statechange.ksh new file mode 100755 index 0000000000..d0bc11eee9 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/statechange.ksh @@ -0,0 +1,41 @@ +#!/bin/ksh -p +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018 Joyent, Inc. All rights reserved. +# + +# Do we support vrrp? +# jst_vrrp="yes" + +# Do we support unfiltered promiscuous mode +# jst_ufpromisc="yes" + +# Do we use vnd devices +# jst_createvnd="yes" + +# Do we not need to have various SMF and ccs stuff created for us? +jst_simplefs="yes" + +# Should we show snapshots? +jst_showsnap="yes" + +# Where does the mdata socket live? +jst_mdatapath="/native/.zonecontrol/" + +# Ensure docker zfd(7D) log is not put in the wrong place +if [[ "$_ZONECFG_attr_docker" == true ]]; then + typeset -A FORCED_ATTRS + FORCED_ATTRS["zlog-name"]= +fi + +. /usr/lib/brand/jcommon/statechange diff --git a/usr/src/lib/brand/shared/zone/common.ksh b/usr/src/lib/brand/shared/zone/common.ksh index 3857a806cf..9d1bbbb2d6 100644 --- a/usr/src/lib/brand/shared/zone/common.ksh +++ b/usr/src/lib/brand/shared/zone/common.ksh @@ -19,6 +19,7 @@ # CDDL HEADER END # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014, Joyent, Inc. All rights reserved. # # @@ -96,9 +97,27 @@ vlog() safe_dir() { typeset dir="$1" + typeset pwd_dir="" - if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then - fatal "$e_baddir" "$dir" + if [[ -d $ZONEROOT/$dir ]]; then + if [[ -h $ZONEROOT/$dir ]]; then + # + # When dir is a symlink to a directory, we 'cd' to that + # directory to ensure that's under $ZONEROOT. We use pwd + # from /usr/bin instead of built-in because they give + # different results. + # + pwd_dir=$(cd $ZONEROOT/$dir && /usr/bin/pwd) + if [[ $pwd_dir =~ "^$ZONEROOT" ]]; then + return; + else + fatal \ + "$e_baddir: symlink out of zoneroot" "$dir" + fi + else + # it's a dir and not a symlink, so that's ok. + return + fi fi } @@ -109,9 +128,7 @@ safe_opt_dir() [[ ! -e $ZONEROOT/$dir ]] && return - if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then - fatal "$e_baddir" "$dir" - fi + safe_dir $dir } # Only make a copy if we haven't already done so. @@ -187,7 +204,7 @@ safe_replace() fi cat <<-END >$filename || exit 1 - #!/bin/sh -p + #!/bin/sh # # Solaris Brand Replacement # @@ -333,10 +350,12 @@ post_unpack() # # Check if the image was created with a valid libc.so.1. # - hwcap=`moe -v -32 $ZONEROOT/lib/libc.so.1 2>&1` - if (( $? != 0 )); then - vlog "$f_hwcap_info" "$hwcap" - fail_fatal "$f_sanity_hwcap" + if [[ -f $ZONEROOT/lib/libc.so.1 ]]; then + hwcap=`moe -v -32 $ZONEROOT/lib/libc.so.1 2>&1` + if (( $? != 0 )); then + vlog "$f_hwcap_info" "$hwcap" + fail_fatal "$f_sanity_hwcap" + fi fi ( cd "$ZONEROOT" && \ @@ -1003,41 +1022,36 @@ install_image() return 0 } -# Setup i18n output -TEXTDOMAIN="SUNW_OST_OSCMD" -export TEXTDOMAIN - -e_cannot_wrap=$(gettext "%s: error: wrapper file already exists") -e_baddir=$(gettext "Invalid '%s' directory within the zone") -e_badfile=$(gettext "Invalid '%s' file within the zone") -e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.") -e_not_found=$(gettext "%s: error: file or directory not found.") -e_install_abort=$(gettext "Installation aborted.") -e_not_readable=$(gettext "Cannot read directory '%s'") -e_not_dir=$(gettext "Error: must be a directory") -e_unknown_archive=$(gettext "Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive.") -e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.") -e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).") -e_tmpfile=$(gettext "Unable to create temporary file") -e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.") -f_mkdir=$(gettext "Unable to create directory %s.") -f_chmod=$(gettext "Unable to chmod directory %s.") -f_chown=$(gettext "Unable to chown directory %s.") -f_hwcap_info=$(gettext "HWCAP: %s\n") -f_sanity_hwcap=$(gettext \ -"The image was created with an incompatible libc.so.1 hwcap lofs mount.\n"\ +e_cannot_wrap="%s: error: wrapper file already exists" +e_baddir="Invalid '%s' directory within the zone" +e_badfile="Invalid '%s' file within the zone" +e_path_abs="Pathname specified to -a '%s' must be absolute." +e_not_found="%s: error: file or directory not found." +e_install_abort="Installation aborted." +e_not_readable="Cannot read directory '%s'" +e_not_dir="Error: must be a directory" +e_unknown_archive="Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive." +e_absolute_archive="Error: archive contains absolute paths instead of relative paths." +e_mismatch_archive="Error: the archive top-level directory (%s) does not match the zonepath (%s)." +e_tmpfile="Unable to create temporary file" +e_root_full="Zonepath root %s exists and contains data; remove or move aside prior to install." +f_mkdir="Unable to create directory %s." +f_chmod="Unable to chmod directory %s." +f_chown="Unable to chown directory %s." +f_hwcap_info="HWCAP: %s\n" +f_sanity_hwcap="The image was created with an incompatible libc.so.1 hwcap lofs mount.\n"\ " The zone will not boot on this platform. See the zone's\n"\ -" documentation for the recommended way to create the archive.") +" documentation for the recommended way to create the archive." -m_analyse_archive=$(gettext "Analysing the archive") +m_analyse_archive="Analysing the archive" -not_readable=$(gettext "Cannot read file '%s'") -not_flar=$(gettext "Input is not a flash archive") -bad_flar=$(gettext "Flash archive is a corrupt") -bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.") -f_unpack_failed=$(gettext "Unpacking the archive failed") -unknown_archiver=$(gettext "Archiver %s is not supported") -cmd_not_exec=$(gettext "Required command '%s' not executable!") +not_readable="Cannot read file '%s'" +not_flar="Input is not a flash archive" +bad_flar="Flash archive is a corrupt" +bad_zfs_flar="Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax." +f_unpack_failed="Unpacking the archive failed" +unknown_archiver="Archiver %s is not supported" +cmd_not_exec="Required command '%s' not executable!" # # Exit values used by the script, as #defined in <sys/zone.h> diff --git a/usr/src/lib/brand/shared/zone/uninstall.ksh b/usr/src/lib/brand/shared/zone/uninstall.ksh index 468a7ed92f..923363c864 100644 --- a/usr/src/lib/brand/shared/zone/uninstall.ksh +++ b/usr/src/lib/brand/shared/zone/uninstall.ksh @@ -35,31 +35,31 @@ bname=`basename $0` # # error messages # -m_usage=$(gettext "Usage: %s: [-hFn]") - -m_1_zfs_promote=$(gettext "promoting '%s'.") -m_1_zfs_destroy=$(gettext "destroying '%s'.") -m_2_zfs_rename=$(gettext "renaming '%s' to '%s'.") -m_3_zfs_set=$(gettext "setting property %s='%s' for '%s'.") -m_rm_r=$(gettext "recursively deleting '%s'.") -m_rm=$(gettext "deleting '%s'.") - -w_no_ds=$(gettext "Warning: no zonepath dataset found.") - -f_usage_err=$(gettext "Error: invalid usage") -f_abort=$(gettext "Error: internal error detected, aborting.") -f_1_zfs_promote=$(gettext "Error: promoting ZFS dataset '%s'.") -f_2_zfs_rename=$(gettext "Error: renaming ZFS dataset '%s' to '%s'.") -f_3_zfs_set=$(gettext "Error: setting ZFS propery %s='%s' for '%s'.") -f_1_zfs_destroy=$(gettext "Error: destroying ZFS dataset.") -f_2_zfs_get=$(gettext "Error: reading ZFS dataset property '%s' from '%s'.") -f_user_snap=$(gettext "Error: user snapshot(s) detected.") -f_stray_snap=$(gettext "Error: uncloned snapshot(s) detected.") -f_stray_clone=$(gettext "Error: cloned zone datasets found outsize of zone.") -f_rm_snap=$(gettext "Error: please delete snapshot(s) and retry uninstall.") -f_rm_clone=$(gettext "Error: please delete clone(s) and retry uninstall.") -f_iu_clone=$(gettext "Error: cloned zone dataset(s) in use.") -f_dis_clone=$(gettext "Error: please stop using clone(s) and retry uninstall.") +m_usage="Usage: %s: [-hFn]" + +m_1_zfs_promote="promoting '%s'." +m_1_zfs_destroy="destroying '%s'." +m_2_zfs_rename="renaming '%s' to '%s'." +m_3_zfs_set="setting property %s='%s' for '%s'." +m_rm_r="recursively deleting '%s'." +m_rm="deleting '%s'." + +w_no_ds="Warning: no zonepath dataset found." + +f_usage_err="Error: invalid usage" +f_abort="Error: internal error detected, aborting." +f_1_zfs_promote="Error: promoting ZFS dataset '%s'." +f_2_zfs_rename="Error: renaming ZFS dataset '%s' to '%s'." +f_3_zfs_set="Error: setting ZFS propery %s='%s' for '%s'." +f_1_zfs_destroy="Error: destroying ZFS dataset." +f_2_zfs_get="Error: reading ZFS dataset property '%s' from '%s'." +f_user_snap="Error: user snapshot(s) detected." +f_stray_snap="Error: uncloned snapshot(s) detected." +f_stray_clone="Error: cloned zone datasets found outsize of zone." +f_rm_snap="Error: please delete snapshot(s) and retry uninstall." +f_rm_clone="Error: please delete clone(s) and retry uninstall." +f_iu_clone="Error: cloned zone dataset(s) in use." +f_dis_clone="Error: please stop using clone(s) and retry uninstall." # # functions diff --git a/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c b/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c index a637542c62..33c68f4a05 100644 --- a/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c +++ b/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright 2019 Joyent, Inc. + */ + #include <strings.h> #include <stdlib.h> #include <netdir.h> @@ -1027,7 +1031,7 @@ fmd_adm_xprt_iter(fmd_adm_t *ap, fmd_adm_xprt_f *func, void *arg) } for (i = 0; i < rxl.rxl_len; i++) - func(rxl.rxl_buf.rxl_buf_val[i], arg); + (void) func(rxl.rxl_buf.rxl_buf_val[i], arg); xdr_free(xdr_fmd_rpc_xprtlist, (char *)&rxl); return (0); diff --git a/usr/src/lib/fm/libfmd_snmp/Makefile.com b/usr/src/lib/fm/libfmd_snmp/Makefile.com index 99dd888363..e0abeeef0d 100644 --- a/usr/src/lib/fm/libfmd_snmp/Makefile.com +++ b/usr/src/lib/fm/libfmd_snmp/Makefile.com @@ -53,7 +53,7 @@ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS) CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS) -SNMPLIBS = -lnetsnmp -lnetsnmpagent +SNMPLIBS = -lnetsnmp -lnetsnmpagent -lnetsnmphelpers NATIVE_LIBS += libnetsnmp.so libnetsnmpagent.so LDLIBS += $(MACH_LDLIBS) diff --git a/usr/src/lib/fm/topo/libtopo/common/libtopo.h b/usr/src/lib/fm/topo/libtopo/common/libtopo.h index 8094587fdf..02d7828a79 100644 --- a/usr/src/lib/fm/topo/libtopo/common/libtopo.h +++ b/usr/src/lib/fm/topo/libtopo/common/libtopo.h @@ -410,6 +410,8 @@ extern void topo_hdl_free(topo_hdl_t *, void *, size_t); extern int topo_hdl_nvalloc(topo_hdl_t *, nvlist_t **, uint_t); extern int topo_hdl_nvdup(topo_hdl_t *, nvlist_t *, nvlist_t **); extern char *topo_hdl_strdup(topo_hdl_t *, const char *); +extern char *topo_hdl_strsplit(topo_hdl_t *, const char *, const char *, + char **); /* * Interfaces for interacting with directed graph topologies diff --git a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers index 5d1b53f1f6..ca33084ff2 100644 --- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers +++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers @@ -86,6 +86,7 @@ SYMBOL_VERSION SUNWprivate { topo_hdl_strdup; topo_hdl_strfree; topo_hdl_strfreev; + topo_hdl_strsplit; topo_hdl_zalloc; topo_led_state_name; topo_led_type_name; @@ -101,6 +102,8 @@ SYMBOL_VERSION SUNWprivate { topo_mod_alloc; topo_mod_auth; topo_mod_clean_str; + topo_mod_create_ufm; + topo_mod_create_ufm_slot; topo_mod_clrdebug; topo_mod_cpufmri; topo_mod_create_ufm; @@ -137,6 +140,7 @@ SYMBOL_VERSION SUNWprivate { topo_mod_strdup; topo_mod_strfree; topo_mod_strfreev; + topo_mod_strsplit; topo_mod_unload; topo_mod_unregister; topo_mod_walk_init; diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h index 593f8946fb..1e5589b78c 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h @@ -236,6 +236,27 @@ extern "C" { */ #define TOPO_PROP_IPMI_ENTITY_LIST "entity-list" +/* + * These properties can be used to describe the network configuration of a + * given hardware components. They're currently used to describe the + * network config on the service processor (sp) + */ +#define TOPO_PGROUP_NETCFG "network-config" +#define TOPO_PROP_NETCFG_MACADDR "mac-address" +#define TOPO_PROP_NETCFG_VLAN_ID "vlan-id" +#define TOPO_PROP_NETCFG_IPV4_ADDR "ipv4-address" +#define TOPO_PROP_NETCFG_IPV4_SUBNET "ipv4-subnet" +#define TOPO_PROP_NETCFG_IPV4_GATEWAY "ipv4-gateway" +#define TOPO_PROP_NETCFG_IPV4_TYPE "ipv4-config-type" +#define TOPO_PROP_NETCFG_IPV6_ADDR "ipv6-address" +#define TOPO_PROP_NETCFG_IPV6_ROUTES "ipv6-routes" +#define TOPO_PROP_NETCFG_IPV6_TYPE "ipv6-config-type" + +/* Possible values for TOPO_PROP_NETCFG_TYPE */ +#define TOPO_NETCFG_TYPE_UNKNOWN "unknown" +#define TOPO_NETCFG_TYPE_STATIC "static" +#define TOPO_NETCFG_TYPE_DHCP "dhcp" + #define TOPO_PGROUP_SLOT "slot" #define TOPO_PROP_SLOT_TYPE "slot-type" diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h index 4e2ec816fc..772c51bb0a 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h @@ -222,6 +222,8 @@ extern void topo_mod_free(topo_mod_t *, void *, size_t); extern char *topo_mod_strdup(topo_mod_t *, const char *); extern void topo_mod_strfree(topo_mod_t *, char *); extern void topo_mod_strfreev(topo_mod_t *, char **, uint_t); +extern char *topo_mod_strsplit(topo_mod_t *, const char *, const char *, + char **); extern int topo_mod_nvalloc(topo_mod_t *, nvlist_t **, uint_t); extern int topo_mod_nvdup(topo_mod_t *, nvlist_t *, nvlist_t **); diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_string.c b/usr/src/lib/fm/topo/libtopo/common/topo_string.c index 53a6280647..bca2bdc66a 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_string.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_string.c @@ -66,6 +66,58 @@ topo_hdl_strfreev(topo_hdl_t *thp, char **strarr, uint_t nelem) } char * +topo_hdl_strsplit(topo_hdl_t *hdl, const char *input, const char *sep, + char **lastp) +{ + size_t seplen = strlen(sep); + const char *scanstart; + char *token; + char *ret; + + if (input != NULL) { + /* + * Start scanning at beginning of input: + */ + scanstart = input; + } else if (*lastp == NULL) { + /* + * If we have already finished scanning, return NULL. + */ + return (NULL); + } else { + /* + * Otherwise, start scanning where we left off: + */ + scanstart = *lastp; + } + + token = strstr(scanstart, sep); + if (token != NULL) { + /* + * We still have a separator, so advance the next-start + * pointer past it: + */ + *lastp = token + seplen; + /* + * Copy out this element. The buffer must fit the string + * exactly, so that topo_hdl_strfree() can determine its + * size with strlen(). + */ + ret = topo_hdl_alloc(hdl, token - scanstart + 1); + (void) strncpy(ret, scanstart, token - scanstart); + ret[token - scanstart] = '\0'; + } else { + /* + * We have no separator, so this is the last element: + */ + *lastp = NULL; + ret = topo_hdl_strdup(hdl, scanstart); + } + + return (ret); +} + +char * topo_mod_strdup(topo_mod_t *mod, const char *s) { return (topo_hdl_strdup(mod->tm_hdl, s)); @@ -83,6 +135,13 @@ topo_mod_strfreev(topo_mod_t *mod, char **strarr, uint_t nelem) topo_hdl_strfreev(mod->tm_hdl, strarr, nelem); } +char * +topo_mod_strsplit(topo_mod_t *mod, const char *input, const char *sep, + char **lastp) +{ + return (topo_hdl_strsplit(mod->tm_hdl, input, sep, lastp)); +} + const char * topo_strbasename(const char *s) { diff --git a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh index 874bad142f..874bad142f 100644..100755 --- a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh +++ b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh diff --git a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml index 6b16063427..9362ae3e8d 100644 --- a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml @@ -11,7 +11,7 @@ source. A copy of the CDDL is also available via the Internet at http://www.illumos.org/license/CDDL. - Copyright (c) 2018, Joyent, Inc. + Copyright (c) 2019, Joyent, Inc. --> @@ -201,7 +201,7 @@ <propgroup name='ipmi' version='1' name-stability='Private' data-stability='Private' > <propval name='entity-list' type='string_array' > - <propitem value='P2-DIMMF1 Temp' /> + <propitem value='P2-DIMMF2 Temp' /> </propval> </propgroup> </node> diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml index 6b16063427..9362ae3e8d 100644 --- a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml @@ -11,7 +11,7 @@ source. A copy of the CDDL is also available via the Internet at http://www.illumos.org/license/CDDL. - Copyright (c) 2018, Joyent, Inc. + Copyright (c) 2019, Joyent, Inc. --> @@ -201,7 +201,7 @@ <propgroup name='ipmi' version='1' name-stability='Private' data-stability='Private' > <propval name='entity-list' type='string_array' > - <propitem value='P2-DIMMF1 Temp' /> + <propitem value='P2-DIMMF2 Temp' /> </propval> </propgroup> </node> diff --git a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml index 0ddcd1e0d8..f6b0a39e9e 100644 --- a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml @@ -83,6 +83,9 @@ Copyright 2019 Joyent, Inc. <range name='hostbridge' min='0' max='254'> <enum-method name='hostbridge' version='1' /> </range> + <range name='sp' min='0' max='0'> + <enum-method name='ipmi' version='1' /> + </range> <range name='usb-mobo' min='0' max='100'> <enum-method name='usb' version='1' /> </range> @@ -161,7 +164,7 @@ Copyright 2019 Joyent, Inc. </range> </set> - <set type='product' setlist='Joyent-Compute-Platform-1101|Joyent-Compute-Platform-1102'> + <set type='product' setlist='Joyent-Compute-Platform-1101|Joyent-Compute-Platform-1102|Joyent-Compute-Platform-2101|Joyent-Compute-Platform-2102'> <range name='psu' min='0' max='1'> <enum-method name='ipmi' version='1' /> </range> diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c b/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c index db853c6695..beae89f9f1 100644 --- a/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c +++ b/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c @@ -32,11 +32,18 @@ #include "disk.h" #include "disk_drivers.h" +/* + * Request the SAS address of the disk (if any) attached to this mpt_sas + * instance at (Enclosure Number, Slot Number). The function returns + * -1 on error and sets errno to ENOENT _only_ if the /devices node + * (*devctl) does not exist. + */ static int get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure, uint32_t slot, char **sas_address) { - int fd, err, i; + int ret = -1, en = ENXIO; + int fd, i; mptsas_get_disk_info_t gdi; mptsas_disk_info_t *di; size_t disz; @@ -44,16 +51,19 @@ get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure, bzero(&gdi, sizeof (gdi)); if ((fd = open(devctl, O_RDWR)) == -1) { + en = errno; topo_mod_dprintf(mod, "could not open '%s' for ioctl: %s\n", devctl, strerror(errno)); + errno = en; return (-1); } if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) { + if (errno != ENOENT) + en = errno; topo_mod_dprintf(mod, "ioctl 1 on '%s' failed: %s\n", devctl, strerror(errno)); - (void) close(fd); - return (-1); + goto out; } gdi.DiskInfoArraySize = disz = sizeof (mptsas_disk_info_t) * @@ -61,19 +71,19 @@ get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure, gdi.PtrDiskInfoArray = di = topo_mod_alloc(mod, disz); if (di == NULL) { topo_mod_dprintf(mod, "memory allocation failed\n"); - (void) close(fd); - return (-1); + en = ENOMEM; + goto out; } if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) { + if (errno != ENOENT) + en = errno; topo_mod_dprintf(mod, "ioctl 2 on '%s' failed: %s\n", devctl, strerror(errno)); topo_mod_free(mod, di, disz); - (void) close(fd); - return (-1); + goto out; } - err = -1; for (i = 0; i < gdi.DiskCount; i++) { if (di[i].Enclosure == enclosure && di[i].Slot == slot) { char sas[17]; /* 16 hex digits and NUL */ @@ -81,14 +91,16 @@ get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure, topo_mod_dprintf(mod, "found mpt_sas disk (%d/%d) " "with adddress %s\n", enclosure, slot, sas); *sas_address = topo_mod_strdup(mod, sas); - err = 0; + en = ret = 0; break; } } topo_mod_free(mod, di, disz); +out: (void) close(fd); - return (err); + errno = en; + return (ret); } int @@ -97,6 +109,8 @@ disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address) char *devctl = NULL; uint32_t enclosure, slot; int err; + char *elem, *lastp; + int ret = -1; /* * Get the required properties from the node. These come from @@ -115,6 +129,35 @@ disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address) return (-1); } - return (get_sas_address(mod, devctl, enclosure, slot, sas_address)); + /* + * devctl is a (potentially) pipe-separated list of different device + * paths to try. + */ + if ((elem = topo_mod_strsplit(mod, devctl, "|", &lastp)) != NULL) { + boolean_t done = B_FALSE; + do { + topo_mod_dprintf(mod, "trying mpt_sas instance at %s\n", + elem); + + ret = get_sas_address(mod, elem, enclosure, + slot, sas_address); + + /* + * Only try further devctl paths from the list if this + * one was not found: + */ + if (ret == 0 || errno != ENOENT) { + done = B_TRUE; + } else { + topo_mod_dprintf(mod, "instance not found\n"); + } + + topo_mod_strfree(mod, elem); + + } while (!done && (elem = topo_mod_strsplit(mod, NULL, "|", + &lastp)) != NULL); + } + topo_mod_strfree(mod, devctl); + return (ret); } diff --git a/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c index a49a131811..115e3b801d 100644 --- a/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c +++ b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c @@ -72,6 +72,12 @@ _topo_fini(topo_mod_t *mod) topo_mod_unregister(mod); } +/* + * Get or set LED state for a particular target attached to an mpt_sas + * instance at (Enclosure Number, Slot Number). The function returns + * -1 on error and sets errno to ENOENT _only_ if the /devices node + * (*devctl) does not exist. + */ static int do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure, uint16_t slot, uint8_t led, uint32_t *ledmode, boolean_t set) @@ -88,8 +94,10 @@ do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure, lc.LedStatus = *ledmode; if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) { + int en = errno; topo_mod_dprintf(mod, "devctl open failed: %s", strerror(errno)); + errno = en; return (-1); } @@ -103,9 +111,11 @@ do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure, */ lc.LedStatus = 0; } else { + int en = errno; topo_mod_dprintf(mod, "led control ioctl failed: %s", strerror(errno)); (void) close(fd); + errno = en; return (-1); } } @@ -113,6 +123,7 @@ do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure, *ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; (void) close(fd); + errno = 0; return (0); } @@ -127,7 +138,8 @@ mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers, char *driver = NULL, *devctl = NULL; uint32_t enclosure, slot; uint8_t mptsas_led; - boolean_t set; + boolean_t set, done; + char *elem, *lastp; if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION) return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); @@ -197,8 +209,41 @@ mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers, topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__); } - if (do_led_control(mod, devctl, enclosure, slot, mptsas_led, &ledmode, - set) != 0) { + /* + * devctl is a (potentially) pipe-separated list of different device + * paths to try. + */ + if ((elem = topo_mod_strsplit(mod, devctl, "|", &lastp)) == NULL) { + topo_mod_dprintf(mod, "%s: could not parse devctl list", + __func__); + ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); + goto out; + } + done = B_FALSE; + do { + topo_mod_dprintf(mod, "%s: trying mpt_sas instance at %s\n", + __func__, elem); + + ret = do_led_control(mod, elem, enclosure, slot, + mptsas_led, &ledmode, set); + + /* + * Only try further devctl paths from the list if this one + * was not found: + */ + if (ret == 0 || errno != ENOENT) { + done = B_TRUE; + } else { + topo_mod_dprintf(mod, "%s: instance not found\n", + __func__); + } + + topo_mod_strfree(mod, elem); + + } while (!done && (elem = topo_mod_strsplit(mod, NULL, "|", + &lastp)) != NULL); + + if (ret != 0) { topo_mod_dprintf(mod, "%s: do_led_control failed", __func__); ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); goto out; diff --git a/usr/src/lib/fm/topo/modules/common/ipmi/Makefile b/usr/src/lib/fm/topo/modules/common/ipmi/Makefile index b6f2839c42..4a0bf39401 100644 --- a/usr/src/lib/fm/topo/modules/common/ipmi/Makefile +++ b/usr/src/lib/fm/topo/modules/common/ipmi/Makefile @@ -22,7 +22,8 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" +# Copyright (c) 2018, Joyent, Inc. +# MODULE = ipmi CLASS = common @@ -31,4 +32,4 @@ MODULESRCS = ipmi_enum.c ipmi_methods.c include ../../Makefile.plugin -LDLIBS += -lipmi +LDLIBS += -lipmi -lnsl diff --git a/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c b/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c index 32b555a3bd..8e867a447b 100644 --- a/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c +++ b/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017, Joyent, Inc. + * Copyright 2019 Joyent, Inc. * Copyright 2019 by Western Digital Corporation */ @@ -30,6 +30,9 @@ #include <fm/topo_mod.h> #include <sys/fm/protocol.h> #include <string.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> #define TOPO_PGROUP_IPMI "ipmi" #define TOPO_PROP_IPMI_ENTITY_REF "entity_ref" @@ -593,6 +596,274 @@ ipmi_check_entity(ipmi_handle_t *ihp, ipmi_entity_t *ep, void *data) return (0); } +static const char * +ipmi2toposrc(uint8_t ipmi_ip_src) +{ + char *cfgtype; + + switch (ipmi_ip_src) { + case (IPMI_LAN_SRC_STATIC): + case (IPMI_LAN_SRC_BIOS): + cfgtype = TOPO_NETCFG_TYPE_STATIC; + break; + case (IPMI_LAN_SRC_DHCP): + cfgtype = TOPO_NETCFG_TYPE_DHCP; + break; + default: + cfgtype = TOPO_NETCFG_TYPE_UNKNOWN; + break; + } + return (cfgtype); +} + +/* + * Channel related IPMI commands reserve 4 bits for the channel number. + */ +#define IPMI_MAX_CHANNEL 0xf + +static int +ipmi_enum_sp(topo_mod_t *mod, tnode_t *pnode) +{ + ipmi_handle_t *ihp; + ipmi_channel_info_t *chinfo; + ipmi_lan_config_t lancfg = { 0 }; + boolean_t found_lan = B_TRUE; + char ipv4_addr[INET_ADDRSTRLEN], subnet[INET_ADDRSTRLEN]; + char gateway[INET_ADDRSTRLEN], macaddr[18]; + char ipv6_addr[INET6_ADDRSTRLEN]; + char **ipv6_routes; + const char *sp_rev, *ipv4_cfgtype, *ipv6_cfgtype; + nvlist_t *auth, *fmri; + tnode_t *sp_node; + topo_pgroup_info_t pgi; + topo_ufm_slot_info_t slotinfo = { 0 }; + int err, ch, i, ret = -1; + + if ((ihp = topo_mod_ipmi_hold(mod)) == NULL) + return (0); + + /* + * If we're able to successfully get the service processor version by + * issuing a GET_DEVICE_ID IPMI command over the KCS interface, then we + * can say with certainty that a service processor exists. If not, + * then either the SP is unresponsive or one isn't present. In either + * case, we bail. + */ + if ((sp_rev = ipmi_firmware_version(ihp)) == NULL) { + topo_mod_dprintf(mod, "failed to query SP"); + topo_mod_ipmi_rele(mod); + return (0); + } + + if ((auth = topo_mod_auth(mod, pnode)) == NULL) { + topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", + topo_mod_errmsg(mod)); + /* errno set */ + goto out; + } + if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, + SP, 0, NULL, auth, NULL, sp_rev, NULL)) == NULL) { + nvlist_free(auth); + topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", + topo_mod_errmsg(mod)); + /* errno set */ + goto out; + } + nvlist_free(auth); + + if ((sp_node = topo_node_bind(mod, pnode, SP, 0, fmri)) == NULL) { + nvlist_free(fmri); + topo_mod_dprintf(mod, "topo_node_bind() failed: %s", + topo_mod_errmsg(mod)); + /* errno set */ + goto out; + } + nvlist_free(fmri); + fmri = NULL; + + if (topo_node_label_set(sp_node, "service-processor", &err) != 0) { + topo_mod_dprintf(mod, "failed to set label on %s=%d: %s", SP, + 0, topo_strerror(err)); + (void) topo_mod_seterrno(mod, err); + goto out; + } + + if (topo_node_fru(pnode, &fmri, NULL, &err) != 0 || + topo_node_fru_set(sp_node, fmri, 0, &err) != 0) { + topo_mod_dprintf(mod, "failed to set FRU on %s=%d: %s", SP, 0, + topo_strerror(err)); + nvlist_free(fmri); + (void) topo_mod_seterrno(mod, err); + goto out; + } + nvlist_free(fmri); + + /* + * Create UFM node to capture the SP LOM version + */ + slotinfo.usi_version = sp_rev; + slotinfo.usi_active = B_TRUE; + slotinfo.usi_mode = TOPO_UFM_SLOT_MODE_NONE; + if (topo_node_range_create(mod, sp_node, UFM, 0, 0) != 0 || + topo_mod_create_ufm(mod, sp_node, + "Baseboard Management Controller firmware", &slotinfo) == NULL) { + topo_mod_dprintf(mod, "failed to create %s node", UFM); + goto out; + } + + /* + * Iterate through the channels to find the LAN channel. + */ + for (ch = 0; ch <= IPMI_MAX_CHANNEL; ch++) { + if ((chinfo = ipmi_get_channel_info(ihp, ch)) != NULL && + chinfo->ici_medium == IPMI_MEDIUM_8023LAN) { + found_lan = B_TRUE; + break; + } + } + /* + * If we found a LAN channel, look up its configuration so that we can + * expose it via node properties. + */ + if (found_lan != B_TRUE || + ipmi_lan_get_config(ihp, ch, &lancfg) != 0) { + (void) fprintf(stderr, "failed to get LAN config\n"); + (void) topo_mod_seterrno(mod, EMOD_UNKNOWN); + goto out; + } + + pgi.tpi_name = TOPO_PGROUP_NETCFG; + pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; + pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; + pgi.tpi_version = TOPO_VERSION; + + if (topo_pgroup_create(sp_node, &pgi, &err) != 0) { + (void) topo_mod_seterrno(mod, err); + goto out; + } + + /* Set MAC address property */ + (void) sprintf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x", + lancfg.ilc_macaddr[0], lancfg.ilc_macaddr[1], + lancfg.ilc_macaddr[2], lancfg.ilc_macaddr[3], + lancfg.ilc_macaddr[4], lancfg.ilc_macaddr[5]); + macaddr[17] = '\0'; + + if (topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_MACADDR, TOPO_PROP_IMMUTABLE, macaddr, + &err) != 0) { + topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s", + SP, 0, topo_strerror(err)); + (void) topo_mod_seterrno(mod, err); + goto out; + } + + /* Set VLAN ID property, if VLAN is enabled */ + if (lancfg.ilc_vlan_enabled == B_TRUE && + topo_prop_set_uint32(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_VLAN_ID, TOPO_PROP_IMMUTABLE, lancfg.ilc_vlan_id, + &err) != 0) { + topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s", + SP, 0, topo_strerror(err)); + (void) topo_mod_seterrno(mod, err); + goto out; + } + + /* Set IPv4 configuration properties if IPv4 is enabled */ + if (lancfg.ilc_ipv4_enabled == B_TRUE && + (inet_ntop(AF_INET, &lancfg.ilc_ipaddr, ipv4_addr, + sizeof (ipv4_addr)) == NULL || + inet_ntop(AF_INET, &lancfg.ilc_subnet, subnet, + sizeof (subnet)) == NULL || + inet_ntop(AF_INET, &lancfg.ilc_gateway_addr, gateway, + sizeof (gateway)) == NULL)) { + (void) fprintf(stderr, "failed to convert IP addresses: %s\n", + strerror(errno)); + (void) topo_mod_seterrno(mod, EMOD_UNKNOWN); + goto out; + } + ipv4_cfgtype = ipmi2toposrc(lancfg.ilc_ipaddr_source); + if (lancfg.ilc_ipv4_enabled == B_TRUE && + (topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV4_ADDR, TOPO_PROP_IMMUTABLE, ipv4_addr, + &err) != 0 || + topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV4_SUBNET, TOPO_PROP_IMMUTABLE, subnet, + &err) != 0 || + topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV4_GATEWAY, TOPO_PROP_IMMUTABLE, gateway, + &err) != 0 || + topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV4_TYPE, TOPO_PROP_IMMUTABLE, ipv4_cfgtype, + &err) != 0)) { + topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s", + SP, 0, topo_strerror(err)); + (void) topo_mod_seterrno(mod, err); + goto out; + } + + /* Set IPv6 configuration properties if IPv6 is enabled */ + if (lancfg.ilc_ipv6_enabled == B_TRUE) { + ipv6_cfgtype = ipmi2toposrc(lancfg.ilc_ipv6_source); + + if (inet_ntop(AF_INET6, &lancfg.ilc_ipv6_addr, ipv6_addr, + sizeof (ipv6_addr)) == NULL) { + (void) fprintf(stderr, "failed to convert IPv6 " + "address: %s\n", strerror(errno)); + (void) topo_mod_seterrno(mod, EMOD_UNKNOWN); + goto out; + } + + /* allocate and populate ipv6-routes string array */ + if ((ipv6_routes = topo_mod_zalloc(mod, + lancfg.ilc_ipv6_nroutes * sizeof (char *))) == NULL) { + /* errno set */ + goto out; + } + for (i = 0; i < lancfg.ilc_ipv6_nroutes; i++) { + if ((ipv6_routes[i] = topo_mod_alloc(mod, + INET6_ADDRSTRLEN)) == NULL) { + /* errno set */ + goto out; + } + } + for (i = 0; i < lancfg.ilc_ipv6_nroutes; i++) { + if (inet_ntop(AF_INET6, &lancfg.ilc_ipv6_routes[i], + ipv6_routes[i], sizeof (ipv6_routes[i])) == NULL) { + (void) fprintf(stderr, "failed to convert " + "IPv6 addresses: %s\n", strerror(errno)); + (void) topo_mod_seterrno(mod, EMOD_UNKNOWN); + goto out; + } + } + } + if (lancfg.ilc_ipv6_enabled == B_TRUE && + (topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV6_ADDR, TOPO_PROP_IMMUTABLE, ipv6_addr, + &err) != 0 || + topo_prop_set_string_array(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV6_ROUTES, TOPO_PROP_IMMUTABLE, + (const char **)ipv6_routes, lancfg.ilc_ipv6_nroutes, &err) != 0 || + topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG, + TOPO_PROP_NETCFG_IPV6_TYPE, TOPO_PROP_IMMUTABLE, ipv6_cfgtype, + &err) != 0)) { + topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s", + SP, 0, topo_strerror(err)); + (void) topo_mod_seterrno(mod, err); + goto out; + } + ret = 0; +out: + if (lancfg.ilc_ipv6_nroutes > 0) { + for (i = 0; i < lancfg.ilc_ipv6_nroutes; i++) + topo_mod_free(mod, ipv6_routes[i], INET6_ADDRSTRLEN); + topo_mod_free(mod, ipv6_routes, + lancfg.ilc_ipv6_nroutes * sizeof (char *)); + } + topo_mod_ipmi_rele(mod); + return (ret); +} + /* * libtopo enumeration point. This simply iterates over entities looking for * the appropriate type. @@ -607,10 +878,11 @@ ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, int ret; /* - * If the node being passed in ISN'T the chassis node, then we're being - * asked to post-process a statically defined node. + * If the node being passed in ISN'T the chassis or motherboard node, + * then we're being asked to post-process a statically defined node. */ - if (strcmp(topo_node_name(rnode), CHASSIS) != 0) { + if (strcmp(topo_node_name(rnode), CHASSIS) != 0 && + strcmp(topo_node_name(rnode), MOTHERBOARD) != 0) { if (ipmi_post_process(mod, rnode) != 0) { topo_mod_dprintf(mod, "post processing of node %s=%d " "failed!", topo_node_name(rnode), @@ -620,6 +892,19 @@ ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, return (0); } + /* + * For service processor enumeration we vector off into a special code + * path. + */ + if (strcmp(name, SP) == 0) { + if (ipmi_enum_sp(mod, rnode) != 0) { + topo_mod_dprintf(mod, "failed to enumerate the " + "service-processor"); + return (-1); + } + return (0); + } + if (strcmp(name, POWERMODULE) == 0) { data.ed_entity = IPMI_ET_POWER_DOMAIN; } else if (strcmp(name, PSU) == 0) { diff --git a/usr/src/lib/fm/topo/modules/common/ses/ses.c b/usr/src/lib/fm/topo/modules/common/ses/ses.c index 85dc937932..6b9be28bd5 100644 --- a/usr/src/lib/fm/topo/modules/common/ses/ses.c +++ b/usr/src/lib/fm/topo/modules/common/ses/ses.c @@ -340,6 +340,34 @@ static ses_open_fail_list_t *ses_sofh; static pthread_mutex_t ses_sofmt; static void ses_ct_print(char *ptr); +/* + * Static list of enclosure manufacturers/models that we will skip enumerating. + * To skip all models from a particular vendor, set the seb_model field to "*". + */ +typedef struct ses_enc_blacklist { + const char *seb_manuf; + const char *seb_model; +} ses_enc_blacklist_t; + +static const ses_enc_blacklist_t enc_blacklist[] = { + { "LSI", "VirtualSES" } +}; + +#define N_ENC_BLACKLIST (sizeof (enc_blacklist) / \ + sizeof (enc_blacklist[0])) + +static boolean_t +ses_is_blacklisted(ses_enc_blacklist_t *seb) +{ + for (uint_t i = 0; i < N_ENC_BLACKLIST; i++) { + if (strcmp(seb->seb_manuf, enc_blacklist[i].seb_manuf) == 0 && + (strcmp(enc_blacklist[i].seb_model, "*") == 0 || + strcmp(seb->seb_model, enc_blacklist[i].seb_model) == 0)) + return (B_TRUE); + } + return (B_FALSE); +} + static void ses_recheck_dir() { @@ -3298,9 +3326,24 @@ ses_enum_gather(ses_node_t *np, void *data) uint64_t prevstatus, status; boolean_t report; uint64_t subchassis = NO_SUBCHASSIS; + ses_enc_blacklist_t seb; if (ses_node_type(np) == SES_NODE_ENCLOSURE) { /* + * Compare the enclosure identity against the entries in the SES + * enclosure blacklist and ignore it, if found. + */ + verify(nvlist_lookup_string(props, SES_EN_PROP_VID, + (char **)&seb.seb_manuf) == 0); + verify(nvlist_lookup_string(props, SES_EN_PROP_PID, + (char **)&seb.seb_model) == 0); + if (ses_is_blacklisted(&seb) == B_TRUE) { + topo_mod_dprintf(mod, "Skipping enclosure %s-%s: is " + "blacklisted", seb.seb_manuf, seb.seb_model); + return (SES_WALK_ACTION_TERMINATE); + } + + /* * If we have already identified the chassis for this target, * then this is a secondary enclosure and we should ignore it, * along with the rest of the tree (since this is depth-first). diff --git a/usr/src/lib/json_nvlist/json_nvlist.c b/usr/src/lib/json_nvlist/json_nvlist.c index f365c981f3..38d0508e1c 100644 --- a/usr/src/lib/json_nvlist/json_nvlist.c +++ b/usr/src/lib/json_nvlist/json_nvlist.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2015 Joyent, Inc. + * Copyright 2018 Joyent, Inc. */ #include <stdio.h> diff --git a/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com b/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com index 1975547329..f8ad41f345 100644 --- a/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com +++ b/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com @@ -73,8 +73,7 @@ SMATCH=off CFLAGS += $(CCVERBOSE) -I.. DYNFLAGS += $(KRUNPATH) $(KMECHLIB) -znodelete -LDLIBS += -L $(ROOTLIBDIR) -lcrypto -lc -NATIVE_LIBS += libcrypto.so +LDLIBS += -L $(ROOTLIBDIR) -lsunw_crypto -lc ROOTLIBDIR= $(ROOT)/usr/lib/krb5/plugins/preauth diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c index e2a5bf901c..926b7260da 100644 --- a/usr/src/lib/libbe/common/be_list.c +++ b/usr/src/lib/libbe/common/be_list.c @@ -25,6 +25,7 @@ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2011 Joyent, Inc. All rights reserved. * Copyright 2015 Toomas Soome <tsoome@me.com> * Copyright 2015 Gary Mills * Copyright (c) 2016 Martin Matuska. All rights reserved. diff --git a/usr/src/lib/libbrand/common/libbrand.c b/usr/src/lib/libbrand/common/libbrand.c index d0343acc47..dc406d60a0 100644 --- a/usr/src/lib/libbrand/common/libbrand.c +++ b/usr/src/lib/libbrand/common/libbrand.c @@ -323,6 +323,7 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size, const char *curr_zone) { int dst, src; + static char *env_pool = NULL; /* * Walk through the characters, substituting values as needed. @@ -339,6 +340,13 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size, case '%': dst += strlcpy(dbuf + dst, "%", dbuf_size - dst); break; + case 'P': + if (env_pool == NULL) + env_pool = getenv("_ZONEADMD_ZPOOL"); + if (env_pool == NULL) + break; + dst += strlcpy(dbuf + dst, env_pool, dbuf_size - dst); + break; case 'R': if (zonepath == NULL) break; @@ -817,6 +825,7 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename, xmlNodePtr node; xmlChar *special, *dir, *type, *opt; char special_exp[MAXPATHLEN]; + char dir_exp[MAXPATHLEN]; char opt_exp[MAXPATHLEN]; int ret; @@ -843,6 +852,10 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename, special_exp, sizeof (special_exp), zonename, zonepath, NULL, NULL)) != 0) goto next; + if ((ret = i_substitute_tokens((char *)dir, + dir_exp, sizeof (dir_exp), + zonename, zonepath, NULL, NULL)) != 0) + goto next; /* opt might not be defined */ if (strlen((const char *)opt) == 0) { @@ -855,7 +868,7 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename, goto next; } - ret = func(data, (char *)special_exp, (char *)dir, + ret = func(data, (char *)special_exp, (char *)dir_exp, (char *)type, ((opt != NULL) ? opt_exp : NULL)); next: diff --git a/usr/src/lib/libbsm/common/adt.c b/usr/src/lib/libbsm/common/adt.c index d598decc60..20741efa75 100644 --- a/usr/src/lib/libbsm/common/adt.c +++ b/usr/src/lib/libbsm/common/adt.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, Joyent, Inc. All rights reserved. * Copyright 2017 OmniOS Community Edition (OmniOSce) Association. */ @@ -731,7 +732,37 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term) int tries = 3; char msg[512]; int eai_err; + struct ifaddrlist al; + int family; + boolean_t found = B_FALSE; + /* + * getaddrinfo can take a long time to timeout if it can't map the + * hostname to an IP address so try to get an IP address from a local + * interface first. + */ + family = AF_INET6; + if (adt_get_local_address(family, &al) == 0) { + found = B_TRUE; + } else { + family = AF_INET; + if (adt_get_local_address(family, &al) == 0) + found = B_TRUE; + } + + if (found) { + if (family == AF_INET) { + p_term->at_type = AU_IPv4; + (void) memcpy(p_term->at_addr, &al.addr.addr, AU_IPv4); + } else { + p_term->at_type = AU_IPv6; + (void) memcpy(p_term->at_addr, &al.addr.addr6, AU_IPv6); + } + + return (0); + } + + /* Now try getaddrinfo */ while ((tries-- > 0) && ((eai_err = getaddrinfo(hostname, NULL, NULL, &ai)) != 0)) { /* @@ -768,7 +799,9 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term) } freeaddrinfo(ai); return (0); - } else if (auditstate & (AUC_AUDITING | AUC_NOSPACE)) { + } + + if (auditstate & (AUC_AUDITING | AUC_NOSPACE)) { auditinfo_addr_t audit_info; /* @@ -776,58 +809,23 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term) * kernel audit context */ if (auditon(A_GETKAUDIT, (caddr_t)&audit_info, - sizeof (audit_info)) < 0) { - adt_write_syslog("unable to get kernel audit context", - errno); - goto try_interface; + sizeof (audit_info)) >= 0) { + adt_write_syslog("setting Audit IP address to kernel", + 0); + *p_term = audit_info.ai_termid; + return (0); } - adt_write_syslog("setting Audit IP address to kernel", 0); - *p_term = audit_info.ai_termid; - return (0); + adt_write_syslog("unable to get kernel audit context", errno); } -try_interface: - { - struct ifaddrlist al; - int family; - char ntop[INET6_ADDRSTRLEN]; - - /* - * getaddrinfo has failed to map the hostname - * to an IP address, try to get an IP address - * from a local interface. If none up, default - * to loopback. - */ - family = AF_INET6; - if (adt_get_local_address(family, &al) != 0) { - family = AF_INET; - - if (adt_get_local_address(family, &al) != 0) { - adt_write_syslog("adt_get_local_address " - "failed, no Audit IP address available, " - "faking loopback and error", - errno); - IN_SET_LOOPBACK_ADDR( - (struct sockaddr_in *)&(al.addr.addr)); - (void) memcpy(p_term->at_addr, &al.addr.addr, - AU_IPv4); - p_term->at_type = AU_IPv4; - return (-1); - } - } - if (family == AF_INET) { - p_term->at_type = AU_IPv4; - (void) memcpy(p_term->at_addr, &al.addr.addr, AU_IPv4); - } else { - p_term->at_type = AU_IPv6; - (void) memcpy(p_term->at_addr, &al.addr.addr6, AU_IPv6); - } - (void) snprintf(msg, sizeof (msg), "mapping %s to %s", - hostname, inet_ntop(family, &(al.addr), ntop, - sizeof (ntop))); - adt_write_syslog(msg, 0); - return (0); - } + /* No mapping, default to loopback. */ + errno = ENETDOWN; + adt_write_syslog("adt_get_local_address failed, no Audit IP address " + "available, faking loopback and error", errno); + IN_SET_LOOPBACK_ADDR((struct sockaddr_in *)&(al.addr.addr)); + (void) memcpy(p_term->at_addr, &al.addr.addr, AU_IPv4); + p_term->at_type = AU_IPv4; + return (-1); } /* @@ -2122,8 +2120,8 @@ adt_selected(struct adt_event_state *event, au_event_t actual_id, int status) } /* - * Can't map the host name to an IP address in - * adt_get_hostIP. Get something off an interface + * Before trying to map the host name to an IP address in + * adt_get_hostIP, get something off an interface * to act as the hosts IP address for auditing. */ diff --git a/usr/src/lib/libbunyan/Makefile b/usr/src/lib/libbunyan/Makefile new file mode 100644 index 0000000000..a59de91113 --- /dev/null +++ b/usr/src/lib/libbunyan/Makefile @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. +# + +include ../Makefile.lib + +HDRS = bunyan.h +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libbunyan/Makefile.com b/usr/src/lib/libbunyan/Makefile.com new file mode 100644 index 0000000000..d0d00bf0e5 --- /dev/null +++ b/usr/src/lib/libbunyan/Makefile.com @@ -0,0 +1,36 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY = libbunyan.a +VERS = .1 +OBJECTS = bunyan.o +USDT_PROVIDERS = bunyan_provider.d + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lumem -lnvpair +CPPFLAGS += -I../common -I. -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 + +SRCDIR = ../common + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ +include ../../Makefile.usdt diff --git a/usr/src/lib/libbunyan/amd64/Makefile b/usr/src/lib/libbunyan/amd64/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/libbunyan/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libbunyan/common/bunyan.c b/usr/src/lib/libbunyan/common/bunyan.c new file mode 100644 index 0000000000..a442c33cec --- /dev/null +++ b/usr/src/lib/libbunyan/common/bunyan.c @@ -0,0 +1,913 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. + */ + +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include <stdarg.h> +#include <umem.h> +#include <netdb.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/sysmacros.h> +#include <thread.h> +#include <sys/debug.h> + +#include <bunyan.h> +#include <bunyan_provider_impl.h> + +struct bunyan_key; +struct bunyan_stream; +struct bunyan; + +typedef struct bunyan_stream { + struct bunyan_stream *bs_next; + char *bs_name; + bunyan_level_t bs_level; + bunyan_stream_f bs_func; + void *bs_arg; + uint_t bs_count; +} bunyan_stream_t; + +typedef struct bunyan_key { + struct bunyan_key *bk_next; + char *bk_name; + bunyan_type_t bk_type; + void *bk_data; + size_t bk_len; +} bunyan_key_t; + +typedef struct bunyan { + pthread_mutex_t bun_lock; + bunyan_key_t *bun_keys; + bunyan_stream_t *bun_streams; + char *bun_name; + char bun_host[MAXHOSTNAMELEN+1]; +} bunyan_t; + +#define ISO_TIMELEN 25 +static const int bunyan_version = 0; + +static void +bunyan_key_fini(bunyan_key_t *bkp) +{ + size_t nlen = strlen(bkp->bk_name) + 1; + umem_free(bkp->bk_data, bkp->bk_len); + umem_free(bkp->bk_name, nlen); + umem_free(bkp, sizeof (bunyan_key_t)); +} + +static void +bunyan_stream_fini(bunyan_stream_t *bsp) +{ + size_t nlen = strlen(bsp->bs_name) + 1; + umem_free(bsp->bs_name, nlen); + umem_free(bsp, sizeof (bunyan_stream_t)); +} + +int +bunyan_init(const char *name, bunyan_logger_t **bhp) +{ + int ret; + bunyan_t *b; + size_t nlen = strlen(name) + 1; + + b = umem_zalloc(sizeof (bunyan_t), UMEM_DEFAULT); + if (b == NULL) + return (ENOMEM); + + b->bun_name = umem_alloc(nlen, UMEM_DEFAULT); + if (b->bun_name == NULL) { + umem_free(b, sizeof (bunyan_t)); + return (ENOMEM); + } + bcopy(name, b->bun_name, nlen); + + if ((ret = pthread_mutex_init(&b->bun_lock, NULL)) != 0) { + umem_free(b->bun_name, nlen); + umem_free(b, sizeof (bunyan_t)); + return (ret); + } + + VERIFY(gethostname(b->bun_host, sizeof (b->bun_host)) == 0); + b->bun_host[MAXHOSTNAMELEN] = '\0'; + + *bhp = (bunyan_logger_t *)b; + return (0); +} + +void +bunyan_fini(bunyan_logger_t *bhp) +{ + bunyan_t *b = (bunyan_t *)bhp; + bunyan_key_t *bkp; + bunyan_stream_t *bsp; + + while ((bkp = b->bun_keys) != NULL) { + b->bun_keys = bkp->bk_next; + bunyan_key_fini(bkp); + } + + while ((bsp = b->bun_streams) != NULL) { + b->bun_streams = bsp->bs_next; + bunyan_stream_fini(bsp); + } + + if (b->bun_name != NULL) + umem_free(b->bun_name, strlen(b->bun_name) + 1); + + VERIFY(pthread_mutex_destroy(&b->bun_lock) == 0); + umem_free(b, sizeof (bunyan_t)); +} + +/* ARGSUSED */ +int +bunyan_stream_fd(nvlist_t *nvl, const char *js, void *arg) +{ + uintptr_t fd = (uintptr_t)arg; + size_t jslen = strlen(js); + off_t off = 0; + ssize_t ret = 0; + static int maxbuf = -1; + + if (maxbuf == -1) + maxbuf = getpagesize(); + + while (off != jslen) { + /* + * Write up to a page of data at a time. If for some reason an + * individual write fails, move on and try to still write a new + * line at least... + */ + ret = write(fd, js + off, MIN(jslen - off, maxbuf)); + if (ret < 0) + break; + off += ret; + } + + if (ret < 0) { + (void) write(fd, "\n", 1); + } else { + ret = write(fd, "\n", 1); + } + return (ret < 0 ? 1: 0); +} + +int +bunyan_stream_add(bunyan_logger_t *bhp, const char *name, int level, + bunyan_stream_f func, void *arg) +{ + bunyan_stream_t *bs, *cur; + size_t nlen = strlen(name) + 1; + bunyan_t *b = (bunyan_t *)bhp; + + if (level != BUNYAN_L_TRACE && + level != BUNYAN_L_DEBUG && + level != BUNYAN_L_INFO && + level != BUNYAN_L_WARN && + level != BUNYAN_L_ERROR && + level != BUNYAN_L_FATAL) + return (EINVAL); + + bs = umem_alloc(sizeof (bunyan_stream_t), UMEM_DEFAULT); + if (bs == NULL) + return (ENOMEM); + + bs->bs_name = umem_alloc(nlen, UMEM_DEFAULT); + if (bs->bs_name == NULL) { + umem_free(bs, sizeof (bunyan_stream_t)); + return (ENOMEM); + } + bcopy(name, bs->bs_name, nlen); + bs->bs_level = level; + bs->bs_func = func; + bs->bs_arg = arg; + bs->bs_count = 0; + (void) pthread_mutex_lock(&b->bun_lock); + cur = b->bun_streams; + while (cur != NULL) { + if (strcmp(name, cur->bs_name) == 0) { + (void) pthread_mutex_unlock(&b->bun_lock); + umem_free(bs->bs_name, nlen); + umem_free(bs, sizeof (bunyan_stream_t)); + return (EEXIST); + } + cur = cur->bs_next; + } + bs->bs_next = b->bun_streams; + b->bun_streams = bs; + (void) pthread_mutex_unlock(&b->bun_lock); + + return (0); +} + +int +bunyan_stream_remove(bunyan_logger_t *bhp, const char *name) +{ + bunyan_stream_t *cur, *prev; + bunyan_t *b = (bunyan_t *)bhp; + + (void) pthread_mutex_lock(&b->bun_lock); + prev = NULL; + cur = b->bun_streams; + while (cur != NULL) { + if (strcmp(name, cur->bs_name) == 0) + break; + prev = cur; + cur = cur->bs_next; + } + if (cur == NULL) { + (void) pthread_mutex_unlock(&b->bun_lock); + return (ENOENT); + } + if (prev == NULL) + b->bun_streams = cur->bs_next; + else + prev->bs_next = cur->bs_next; + cur->bs_next = NULL; + (void) pthread_mutex_unlock(&b->bun_lock); + + bunyan_stream_fini(cur); + + return (0); +} + +static int +bunyan_key_add_one(bunyan_t *b, const char *name, bunyan_type_t type, + const void *arg) +{ + bunyan_key_t *bkp, *cur, *prev; + size_t nlen = strlen(name) + 1; + size_t blen; + + bkp = umem_alloc(sizeof (bunyan_key_t), UMEM_DEFAULT); + if (bkp == NULL) + return (ENOMEM); + bkp->bk_name = umem_alloc(nlen, UMEM_DEFAULT); + if (bkp->bk_name == NULL) { + umem_free(bkp, sizeof (bunyan_key_t)); + return (ENOMEM); + } + bcopy(name, bkp->bk_name, nlen); + + switch (type) { + case BUNYAN_T_STRING: + blen = strlen(arg) + 1; + break; + case BUNYAN_T_POINTER: + blen = sizeof (uintptr_t); + break; + case BUNYAN_T_IP: + blen = sizeof (struct in_addr); + break; + case BUNYAN_T_IP6: + blen = sizeof (struct in6_addr); + break; + case BUNYAN_T_BOOLEAN: + blen = sizeof (boolean_t); + break; + case BUNYAN_T_INT32: + blen = sizeof (int32_t); + break; + case BUNYAN_T_INT64: + case BUNYAN_T_INT64STR: + blen = sizeof (int64_t); + break; + case BUNYAN_T_UINT32: + blen = sizeof (uint32_t); + break; + case BUNYAN_T_UINT64: + case BUNYAN_T_UINT64STR: + blen = sizeof (uint64_t); + break; + case BUNYAN_T_DOUBLE: + blen = sizeof (double); + break; + default: + umem_free(bkp->bk_name, nlen); + umem_free(bkp, sizeof (bunyan_key_t)); + return (EINVAL); + } + + bkp->bk_data = umem_alloc(blen, UMEM_DEFAULT); + if (bkp->bk_data == NULL) { + umem_free(bkp->bk_name, nlen); + umem_free(bkp, sizeof (bunyan_key_t)); + return (ENOMEM); + } + bcopy(arg, bkp->bk_data, blen); + bkp->bk_len = blen; + bkp->bk_type = type; + + (void) pthread_mutex_lock(&b->bun_lock); + prev = NULL; + cur = b->bun_keys; + while (cur != NULL) { + if (strcmp(name, cur->bk_name) == 0) + break; + prev = cur; + cur = cur->bk_next; + } + if (cur != NULL) { + if (prev == NULL) + b->bun_keys = cur->bk_next; + else + prev->bk_next = cur->bk_next; + bunyan_key_fini(cur); + } + bkp->bk_next = b->bun_keys; + b->bun_keys = bkp; + (void) pthread_mutex_unlock(&b->bun_lock); + + return (0); +} + +static int +bunyan_key_vadd(bunyan_t *b, va_list *ap) +{ + int type, ret; + void *data; + boolean_t bt; + int32_t i32; + int64_t i64; + uint32_t ui32; + uint64_t ui64; + double d; + uintptr_t ptr; + + while ((type = va_arg(*ap, int)) != BUNYAN_T_END) { + const char *name = va_arg(*ap, char *); + + switch (type) { + case BUNYAN_T_STRING: + data = va_arg(*ap, char *); + break; + case BUNYAN_T_POINTER: + ptr = (uintptr_t)va_arg(*ap, void *); + data = &ptr; + break; + case BUNYAN_T_IP: + case BUNYAN_T_IP6: + data = va_arg(*ap, void *); + break; + case BUNYAN_T_BOOLEAN: + bt = va_arg(*ap, boolean_t); + data = &bt; + break; + case BUNYAN_T_INT32: + i32 = va_arg(*ap, int32_t); + data = &i32; + break; + case BUNYAN_T_INT64: + case BUNYAN_T_INT64STR: + i64 = va_arg(*ap, int64_t); + data = &i64; + break; + case BUNYAN_T_UINT32: + ui32 = va_arg(*ap, uint32_t); + data = &ui32; + break; + case BUNYAN_T_UINT64: + case BUNYAN_T_UINT64STR: + ui64 = va_arg(*ap, uint64_t); + data = &ui64; + break; + case BUNYAN_T_DOUBLE: + d = va_arg(*ap, double); + data = &d; + break; + default: + return (EINVAL); + } + + if ((ret = bunyan_key_add_one(b, name, type, data)) != 0) + return (ret); + } + + return (0); +} + +int +bunyan_key_add(bunyan_logger_t *bhp, ...) +{ + int ret; + va_list ap; + bunyan_t *b = (bunyan_t *)bhp; + + va_start(ap, bhp); + ret = bunyan_key_vadd(b, &ap); + va_end(ap); + + return (ret); +} + +int +bunyan_key_remove(bunyan_logger_t *bhp, const char *name) +{ + bunyan_t *b = (bunyan_t *)bhp; + bunyan_key_t *cur, *prev; + + (void) pthread_mutex_lock(&b->bun_lock); + prev = NULL; + cur = b->bun_keys; + while (cur != NULL) { + if (strcmp(name, cur->bk_name) == 0) + break; + prev = cur; + cur = cur->bk_next; + } + + if (cur == NULL) { + (void) pthread_mutex_unlock(&b->bun_lock); + return (ENOENT); + } + + if (prev == NULL) + b->bun_keys = cur->bk_next; + else + prev->bk_next = cur->bk_next; + (void) pthread_mutex_unlock(&b->bun_lock); + + bunyan_key_fini(cur); + return (0); +} + +static bunyan_key_t * +bunyan_key_dup(const bunyan_key_t *bkp) +{ + bunyan_key_t *nkp; + size_t nlen = strlen(bkp->bk_name) + 1; + + nkp = umem_alloc(sizeof (bunyan_key_t), UMEM_DEFAULT); + if (nkp == NULL) + return (NULL); + nkp->bk_next = NULL; + nkp->bk_name = umem_alloc(nlen, UMEM_DEFAULT); + if (nkp->bk_name == NULL) { + umem_free(nkp, sizeof (bunyan_key_t)); + return (NULL); + } + bcopy(bkp->bk_name, nkp->bk_name, nlen); + nkp->bk_type = bkp->bk_type; + nkp->bk_data = umem_alloc(bkp->bk_len, UMEM_DEFAULT); + if (nkp->bk_data == NULL) { + umem_free(nkp->bk_name, nlen); + umem_free(nkp, sizeof (bunyan_key_t)); + return (NULL); + } + bcopy(bkp->bk_data, nkp->bk_data, bkp->bk_len); + nkp->bk_len = bkp->bk_len; + + return (nkp); +} + +static bunyan_stream_t * +bunyan_stream_dup(const bunyan_stream_t *bsp) +{ + bunyan_stream_t *nsp; + size_t nlen = strlen(bsp->bs_name) + 1; + + nsp = umem_alloc(sizeof (bunyan_stream_t), UMEM_DEFAULT); + if (nsp == NULL) + return (NULL); + + nsp->bs_next = NULL; + nsp->bs_name = umem_alloc(nlen, UMEM_DEFAULT); + if (nsp->bs_name == NULL) { + umem_free(nsp, sizeof (bunyan_stream_t)); + return (NULL); + } + bcopy(bsp->bs_name, nsp->bs_name, nlen); + nsp->bs_level = bsp->bs_level; + nsp->bs_func = bsp->bs_func; + nsp->bs_arg = bsp->bs_arg; + nsp->bs_count = 0; + + return (nsp); +} + +static bunyan_t * +bunyan_dup(const bunyan_t *b) +{ + bunyan_t *n; + const bunyan_key_t *bkp; + const bunyan_stream_t *bsp; + size_t nlen; + + n = umem_zalloc(sizeof (bunyan_t), UMEM_DEFAULT); + if (n == NULL) + return (NULL); + + if (pthread_mutex_init(&n->bun_lock, NULL) != 0) { + umem_free(n, sizeof (bunyan_t)); + return (NULL); + } + + for (bkp = b->bun_keys; bkp != NULL; bkp = bkp->bk_next) { + bunyan_key_t *nkp; + nkp = bunyan_key_dup(bkp); + if (nkp == NULL) { + bunyan_fini((bunyan_logger_t *)n); + return (NULL); + } + + nkp->bk_next = n->bun_keys; + n->bun_keys = nkp; + } + + for (bsp = b->bun_streams; bsp != NULL; bsp = bsp->bs_next) { + bunyan_stream_t *nsp; + nsp = bunyan_stream_dup(bsp); + if (bsp == NULL) { + bunyan_fini((bunyan_logger_t *)n); + return (NULL); + } + + nsp->bs_next = n->bun_streams; + n->bun_streams = nsp; + } + + nlen = strlen(b->bun_name) + 1; + n->bun_name = umem_alloc(nlen, UMEM_DEFAULT); + if (n->bun_name == NULL) { + bunyan_fini((bunyan_logger_t *)n); + return (NULL); + } + bcopy(b->bun_name, n->bun_name, nlen); + bcopy(b->bun_host, n->bun_host, MAXHOSTNAMELEN+1); + + return (n); +} + +int +bunyan_child(const bunyan_logger_t *bhp, bunyan_logger_t **outp, ...) +{ + const bunyan_t *b = (const bunyan_t *)bhp; + bunyan_t *n; + va_list ap; + int ret; + + n = bunyan_dup(b); + if (n == NULL) + return (ENOMEM); + + va_start(ap, outp); + ret = bunyan_key_vadd(n, &ap); + va_end(ap); + + if (ret != 0) + bunyan_fini((bunyan_logger_t *)n); + else + *outp = (bunyan_logger_t *)n; + + return (ret); +} + +static int +bunyan_iso_time(char *buf) +{ + struct timeval tv; + struct tm tm; + + if (gettimeofday(&tv, NULL) != 0) + return (errno); + + if (gmtime_r(&tv.tv_sec, &tm) == NULL) + return (errno); + + VERIFY(strftime(buf, ISO_TIMELEN, "%FT%T", &tm) == 19); + + (void) snprintf(&buf[19], 6, ".%03dZ", (int)(tv.tv_usec / 1000)); + + return (0); +} + +/* + * Note, these fields are all required, so even if a user attempts to use one of + * them in their own fields, we'll override them and therefore, have it be the + * last one. + */ +static int +bunyan_vlog_defaults(nvlist_t *nvl, bunyan_t *b, bunyan_level_t level, + const char *msg) +{ + int ret; + char tbuf[ISO_TIMELEN]; + + if ((ret = bunyan_iso_time(tbuf)) != 0) + return (ret); + + if ((ret = nvlist_add_int32(nvl, "v", bunyan_version)) != 0 || + (ret = nvlist_add_int32(nvl, "level", level) != 0) || + (ret = nvlist_add_string(nvl, "name", b->bun_name) != 0) || + (ret = nvlist_add_string(nvl, "hostname", b->bun_host) != 0) || + (ret = nvlist_add_int32(nvl, "pid", getpid()) != 0) || + (ret = nvlist_add_uint32(nvl, "tid", thr_self()) != 0) || + (ret = nvlist_add_string(nvl, "time", tbuf) != 0) || + (ret = nvlist_add_string(nvl, "msg", msg) != 0)) + return (ret); + + return (0); +} + +static int +bunyan_vlog_add(nvlist_t *nvl, const char *key, bunyan_type_t type, void *arg) +{ + int ret; + uintptr_t *up; + struct in_addr *v4; + struct in6_addr *v6; + + /* + * Our buffer needs to hold the string forms of pointers, IPv6 strings, + * etc. INET6_ADDRSTRLEN is large enough for all of these. + */ + char buf[INET6_ADDRSTRLEN]; + + switch (type) { + case BUNYAN_T_STRING: + ret = nvlist_add_string(nvl, key, (char *)arg); + break; + case BUNYAN_T_POINTER: + up = arg; + (void) snprintf(buf, sizeof (buf), "0x%p", *up); + ret = nvlist_add_string(nvl, key, buf); + break; + case BUNYAN_T_IP: + v4 = arg; + VERIFY(inet_ntop(AF_INET, v4, buf, sizeof (buf)) != NULL); + ret = nvlist_add_string(nvl, key, buf); + break; + case BUNYAN_T_IP6: + v6 = arg; + VERIFY(inet_ntop(AF_INET6, v6, buf, sizeof (buf)) != NULL); + ret = nvlist_add_string(nvl, key, buf); + break; + case BUNYAN_T_BOOLEAN: + ret = nvlist_add_boolean_value(nvl, key, *(boolean_t *)arg); + break; + case BUNYAN_T_INT32: + ret = nvlist_add_int32(nvl, key, *(int32_t *)arg); + break; + case BUNYAN_T_INT64: + ret = nvlist_add_int64(nvl, key, *(int64_t *)arg); + break; + case BUNYAN_T_UINT32: + ret = nvlist_add_uint32(nvl, key, *(uint32_t *)arg); + break; + case BUNYAN_T_UINT64: + ret = nvlist_add_uint64(nvl, key, *(uint32_t *)arg); + break; + case BUNYAN_T_DOUBLE: + ret = nvlist_add_double(nvl, key, *(double *)arg); + break; + case BUNYAN_T_INT64STR: + (void) snprintf(buf, sizeof (buf), "%lld", *(int64_t *)arg); + ret = nvlist_add_string(nvl, key, buf); + break; + case BUNYAN_T_UINT64STR: + (void) snprintf(buf, sizeof (buf), "%llu", *(uint64_t *)arg); + ret = nvlist_add_string(nvl, key, buf); + break; + default: + ret = EINVAL; + break; + } + + return (ret); +} + +static int +bunyan_vlog(bunyan_logger_t *bhp, bunyan_level_t level, const char *msg, + va_list *ap) +{ + nvlist_t *nvl = NULL; + int ret, type; + bunyan_key_t *bkp; + bunyan_stream_t *bsp; + char *buf = NULL; + bunyan_t *b = (bunyan_t *)bhp; + + if (msg == NULL) + return (EINVAL); + + (void) pthread_mutex_lock(&b->bun_lock); + + if ((ret = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) { + (void) pthread_mutex_unlock(&b->bun_lock); + return (ret); + } + + /* + * We add pre-defined keys, then go through and process the users keys, + * and finally go ahead and our defaults. If all that succeeds, then we + * can go ahead and call all the built-in logs. + */ + for (bkp = b->bun_keys; bkp != NULL; bkp = bkp->bk_next) { + if ((ret = bunyan_vlog_add(nvl, bkp->bk_name, bkp->bk_type, + bkp->bk_data)) != 0) + goto out; + } + + while ((type = va_arg(*ap, int)) != BUNYAN_T_END) { + void *data; + boolean_t bt; + int32_t i32; + int64_t i64; + uint32_t ui32; + uint64_t ui64; + double d; + uintptr_t ptr; + const char *key = va_arg(*ap, char *); + + switch (type) { + case BUNYAN_T_STRING: + data = va_arg(*ap, char *); + break; + case BUNYAN_T_POINTER: + ptr = (uintptr_t)va_arg(*ap, void *); + data = &ptr; + break; + case BUNYAN_T_IP: + case BUNYAN_T_IP6: + data = va_arg(*ap, void *); + break; + case BUNYAN_T_BOOLEAN: + bt = va_arg(*ap, boolean_t); + data = &bt; + break; + case BUNYAN_T_INT32: + i32 = va_arg(*ap, int32_t); + data = &i32; + break; + case BUNYAN_T_INT64: + case BUNYAN_T_INT64STR: + i64 = va_arg(*ap, int64_t); + data = &i64; + break; + case BUNYAN_T_UINT32: + ui32 = va_arg(*ap, uint32_t); + data = &ui32; + break; + case BUNYAN_T_UINT64: + case BUNYAN_T_UINT64STR: + ui64 = va_arg(*ap, uint64_t); + data = &ui64; + break; + case BUNYAN_T_DOUBLE: + d = va_arg(*ap, double); + data = &d; + break; + default: + ret = EINVAL; + goto out; + } + + if ((ret = bunyan_vlog_add(nvl, key, type, data)) != 0) + goto out; + + } + /* + * This must be the last thing we do before we log to ensure that all of + * our defaults always make it out. + */ + if ((ret = bunyan_vlog_defaults(nvl, b, level, msg)) != 0) + goto out; + + if (nvlist_dump_json(nvl, &buf) < 0) { + ret = errno; + goto out; + } + + /* Fire DTrace probes */ + switch (level) { + case BUNYAN_L_TRACE: + BUNYAN_LOG_TRACE(buf); + break; + case BUNYAN_L_DEBUG: + BUNYAN_LOG_DEBUG(buf); + break; + case BUNYAN_L_INFO: + BUNYAN_LOG_INFO(buf); + break; + case BUNYAN_L_WARN: + BUNYAN_LOG_WARN(buf); + break; + case BUNYAN_L_ERROR: + BUNYAN_LOG_ERROR(buf); + break; + case BUNYAN_L_FATAL: + BUNYAN_LOG_FATAL(buf); + break; + } + + for (bsp = b->bun_streams; bsp != NULL; bsp = bsp->bs_next) { + if (bsp->bs_level <= level) + if (bsp->bs_func(nvl, buf, bsp->bs_arg) != 0) + bsp->bs_count++; + } + ret = 0; +out: + (void) pthread_mutex_unlock(&b->bun_lock); + if (buf != NULL) + nvlist_dump_json_free(nvl, buf); + if (nvl != NULL) + nvlist_free(nvl); + return (ret); +} + +int +bunyan_trace(bunyan_logger_t *bhp, const char *msg, ...) +{ + va_list va; + int ret; + + va_start(va, msg); + ret = bunyan_vlog(bhp, BUNYAN_L_TRACE, msg, &va); + va_end(va); + + return (ret); +} + +int +bunyan_debug(bunyan_logger_t *bhp, const char *msg, ...) +{ + va_list va; + int ret; + + va_start(va, msg); + ret = bunyan_vlog(bhp, BUNYAN_L_DEBUG, msg, &va); + va_end(va); + + return (ret); +} + +int +bunyan_info(bunyan_logger_t *bhp, const char *msg, ...) +{ + va_list va; + int ret; + + va_start(va, msg); + ret = bunyan_vlog(bhp, BUNYAN_L_INFO, msg, &va); + va_end(va); + + return (ret); +} + +int +bunyan_warn(bunyan_logger_t *bhp, const char *msg, ...) +{ + va_list va; + int ret; + + va_start(va, msg); + ret = bunyan_vlog(bhp, BUNYAN_L_WARN, msg, &va); + va_end(va); + + return (ret); +} + +int +bunyan_error(bunyan_logger_t *bhp, const char *msg, ...) +{ + va_list va; + int ret; + + va_start(va, msg); + ret = bunyan_vlog(bhp, BUNYAN_L_ERROR, msg, &va); + va_end(va); + + return (ret); +} + + +int +bunyan_fatal(bunyan_logger_t *bhp, const char *msg, ...) +{ + va_list va; + int ret; + + va_start(va, msg); + ret = bunyan_vlog(bhp, BUNYAN_L_FATAL, msg, &va); + va_end(va); + + return (ret); +} diff --git a/usr/src/lib/libbunyan/common/bunyan.h b/usr/src/lib/libbunyan/common/bunyan.h new file mode 100644 index 0000000000..9a01f6f6cd --- /dev/null +++ b/usr/src/lib/libbunyan/common/bunyan.h @@ -0,0 +1,88 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014, Joyent, Inc. + */ + +#ifndef _BUNYAN_H +#define _BUNYAN_H + +/* + * C version of the bunyan logging format. + */ + +#include <limits.h> +#include <libnvpair.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct bunyan_logger bunyan_logger_t; + +typedef enum bunyan_level { + BUNYAN_L_TRACE = 10, + BUNYAN_L_DEBUG = 20, + BUNYAN_L_INFO = 30, + BUNYAN_L_WARN = 40, + BUNYAN_L_ERROR = 50, + BUNYAN_L_FATAL = 60 +} bunyan_level_t; + +typedef enum bunyan_type { + BUNYAN_T_END = 0x0, + BUNYAN_T_STRING, + BUNYAN_T_POINTER, + BUNYAN_T_IP, + BUNYAN_T_IP6, + BUNYAN_T_BOOLEAN, + BUNYAN_T_INT32, + BUNYAN_T_INT64, + BUNYAN_T_UINT32, + BUNYAN_T_UINT64, + BUNYAN_T_DOUBLE, + BUNYAN_T_INT64STR, + BUNYAN_T_UINT64STR +} bunyan_type_t; + +/* + * A handle is MT-safe, but not fork-safe. + */ +extern int bunyan_init(const char *, bunyan_logger_t **); +extern int bunyan_child(const bunyan_logger_t *, bunyan_logger_t **, ...); +extern void bunyan_fini(bunyan_logger_t *); + +/* + * Bunyan stream callbacks are guaranteed to be serialized. + */ +typedef int (*bunyan_stream_f)(nvlist_t *, const char *, void *); +extern int bunyan_stream_fd(nvlist_t *, const char *, void *); + +extern int bunyan_stream_add(bunyan_logger_t *, const char *, int, + bunyan_stream_f, void *); +extern int bunyan_stream_remove(bunyan_logger_t *, const char *); + +extern int bunyan_key_add(bunyan_logger_t *, ...); +extern int bunyan_key_remove(bunyan_logger_t *, const char *); + +extern int bunyan_trace(bunyan_logger_t *, const char *msg, ...); +extern int bunyan_debug(bunyan_logger_t *, const char *msg, ...); +extern int bunyan_info(bunyan_logger_t *, const char *msg, ...); +extern int bunyan_warn(bunyan_logger_t *, const char *msg, ...); +extern int bunyan_error(bunyan_logger_t *, const char *msg, ...); +extern int bunyan_fatal(bunyan_logger_t *, const char *msg, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _BUNYAN_H */ diff --git a/usr/src/lib/libbunyan/common/bunyan_provider.d b/usr/src/lib/libbunyan/common/bunyan_provider.d new file mode 100644 index 0000000000..d47ea75733 --- /dev/null +++ b/usr/src/lib/libbunyan/common/bunyan_provider.d @@ -0,0 +1,32 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014, Joyent, Inc. + */ + +/* + * Bunyan DTrace provider + */ +provider bunyan { + probe log__trace(char *); + probe log__debug(char *); + probe log__info(char *); + probe log__warn(char *); + probe log__error(char *); + probe log__fatal(char *); +}; + +#pragma D attributes Stable/Stable/ISA provider bunyan provider +#pragma D attributes Private/Private/Unknown provider bunyan module +#pragma D attributes Private/Private/Unknown provider bunyan function +#pragma D attributes Stable/Stable/ISA provider bunyan name +#pragma D attributes Stable/Stable/ISA provider bunyan args diff --git a/usr/src/lib/libbunyan/common/llib-lbunyan b/usr/src/lib/libbunyan/common/llib-lbunyan new file mode 100644 index 0000000000..31f6a52aba --- /dev/null +++ b/usr/src/lib/libbunyan/common/llib-lbunyan @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <bunyan.h> diff --git a/usr/src/lib/libbunyan/common/mapfile-vers b/usr/src/lib/libbunyan/common/mapfile-vers new file mode 100644 index 0000000000..775af4ab45 --- /dev/null +++ b/usr/src/lib/libbunyan/common/mapfile-vers @@ -0,0 +1,53 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOS_1.0 { + global: + bunyan_init; + bunyan_child; + bunyan_fini; + bunyan_stream_fd; + bunyan_stream_add; + bunyan_stream_remove; + bunyan_key_add; + bunyan_key_remove; + bunyan_trace; + bunyan_debug; + bunyan_info; + bunyan_warn; + bunyan_error; + bunyan_fatal; +}; + +SYMBOL_VERSION ILLUMOSprivate { + local: + *; +}; diff --git a/usr/src/lib/libbunyan/i386/Makefile b/usr/src/lib/libbunyan/i386/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/libbunyan/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libbunyan/sparc/Makefile b/usr/src/lib/libbunyan/sparc/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/libbunyan/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libbunyan/sparcv9/Makefile b/usr/src/lib/libbunyan/sparcv9/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/libbunyan/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libc/Makefile b/usr/src/lib/libc/Makefile index 831425ecfd..ebc1338484 100644 --- a/usr/src/lib/libc/Makefile +++ b/usr/src/lib/libc/Makefile @@ -25,6 +25,8 @@ # Use is subject to license terms. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # +# Copyright (c) 2018, Joyent, Inc. +# LIBBASENAME= libc LIBRARY= $(LIBBASENAME:%=%.a) @@ -130,7 +132,7 @@ all: all_h lib32 $(BUILD64) .WAIT lib64 .WAIT etc THIRDPARTYLICENSE THIRDPARTYLICENSE: extract-copyright $(RM) $@ - ./extract-copyright . > $@ + $(PERL) ./extract-copyright . > $@ etc: $($(MACH)_ETC) diff --git a/usr/src/lib/libc/Makefile.targ b/usr/src/lib/libc/Makefile.targ index 326cca9bf4..9b2d6d35bd 100644 --- a/usr/src/lib/libc/Makefile.targ +++ b/usr/src/lib/libc/Makefile.targ @@ -318,4 +318,25 @@ pics/%.o: $(LIBCBASE)/../port/threads/%.d $(THREADSOBJS:%=pics/%) $(COMPILE.d) -C -xlazyload -s $< -o $@ $(THREADSOBJS:%=pics/%) $(POST_PROCESS_O) +# assym rules + +LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) + +# +# genassym is a funny fish: it's run on the build machine, so should use the +# native compilers, but its job is to reflect the proto area header definitions, +# so we need to use $(CPPFLAGS) not $(CPPFLAGS.native) to make sure we pick up +# those headers. +# +genassym: $(GENASSYM_C) + $(NATIVECC) $(NATIVE_CFLAGS) $(CPPFLAGS) $(LDFLAGS.native) \ + -o $@ $(GENASSYM_C) + +# shared between 32- and 64-bit +OFFSETS = $(LIBCDIR)/$(MACH)/offsets.in + +assym.h: $(OFFSETS) genassym + $(OFFSETS_CREATE) <$(OFFSETS) >$@ + ./genassym >>$@ + include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile index 25ae31284c..c21ef76ee2 100644 --- a/usr/src/lib/libc/amd64/Makefile +++ b/usr/src/lib/libc/amd64/Makefile @@ -903,6 +903,7 @@ PORTSYS= \ fcntl.o \ getpagesizes.o \ getpeerucred.o \ + inotify.o \ inst_sync.o \ issetugid.o \ label.o \ @@ -1228,17 +1229,6 @@ $(ASSYMDEP_OBJS:%=pics/%): assym.h # assym.h build rules GENASSYM_C = genassym.c -LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) - -genassym: $(GENASSYM_C) - $(NATIVECC) $(NATIVE_CFLAGS) -Iinc -I$(LIBCDIR)/inc $(CPPFLAGS.native) \ - $(LDFLAGS.native) -o $@ $(GENASSYM_C) - -OFFSETS = $(LIBCDIR)/$(MACH)/offsets.in - -assym.h: $(OFFSETS) genassym - $(OFFSETS_CREATE) <$(OFFSETS) >$@ - ./genassym >>$@ # derived C source and related explicit dependencies $(LIBCDIR)/port/gen/errlst.c + \ diff --git a/usr/src/lib/libc/amd64/gen/siginfolst.c b/usr/src/lib/libc/amd64/gen/siginfolst.c index 8451dfbb4f..8b8a1b4669 100644 --- a/usr/src/lib/libc/amd64/gen/siginfolst.c +++ b/usr/src/lib/libc/amd64/gen/siginfolst.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = { 0, 0, 0, 0, 0, 0, /* SIGRTMIN+15 */ + 0, 0, /* SIGRTMIN+16 */ 0, 0, /* SIGRTMAX-15 */ 0, 0, 0, 0, diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com index 17bd421383..d9e56ee8ec 100644 --- a/usr/src/lib/libc/i386/Makefile.com +++ b/usr/src/lib/libc/i386/Makefile.com @@ -943,6 +943,7 @@ PORTSYS= \ fcntl.o \ getpagesizes.o \ getpeerucred.o \ + inotify.o \ inst_sync.o \ issetugid.o \ label.o \ @@ -1304,21 +1305,7 @@ $(ASSYMDEP_OBJS:%=pics/%) := CPPFLAGS += -I. $(ASSYMDEP_OBJS:%=pics/%): assym.h -# assym.h build rules - GENASSYM_C = $(LIBCDIR)/$(MACH)/genassym.c -LDFLAGS.native = $(LDASSERTS) $(ZASSERTDEFLIB)=libc.so $(BDIRECT) - -genassym: $(GENASSYM_C) - $(NATIVECC) $(NATIVE_CFLAGS) -I$(LIBCBASE)/inc -I$(LIBCDIR)/inc \ - -D__EXTENSIONS__ $(CPPFLAGS.native) $(LDFLAGS.native) \ - -o $@ $(GENASSYM_C) - -OFFSETS = $(LIBCDIR)/$(MACH)/offsets.in - -assym.h: $(OFFSETS) genassym - $(OFFSETS_CREATE) <$(OFFSETS) >$@ - ./genassym >>$@ # derived C source and related explicit dependencies $(LIBCDIR)/port/gen/errlst.c + \ diff --git a/usr/src/lib/libc/i386/gen/siginfolst.c b/usr/src/lib/libc/i386/gen/siginfolst.c index 8451dfbb4f..8b8a1b4669 100644 --- a/usr/src/lib/libc/i386/gen/siginfolst.c +++ b/usr/src/lib/libc/i386/gen/siginfolst.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = { 0, 0, 0, 0, 0, 0, /* SIGRTMIN+15 */ + 0, 0, /* SIGRTMIN+16 */ 0, 0, /* SIGRTMAX-15 */ 0, 0, 0, 0, diff --git a/usr/src/lib/libc/inc/thr_inlines.h b/usr/src/lib/libc/inc/thr_inlines.h index 1259816e63..872b23c5de 100644 --- a/usr/src/lib/libc/inc/thr_inlines.h +++ b/usr/src/lib/libc/inc/thr_inlines.h @@ -51,17 +51,19 @@ extern __GNU_INLINE ulwp_t * _curthread(void) { -#if defined(__amd64) ulwp_t *__value; - __asm__ __volatile__("movq %%fs:0, %0" : "=r" (__value)); + __asm__ __volatile__( +#if defined(__amd64) + "movq %%fs:0, %0\n\t" #elif defined(__i386) - ulwp_t *__value; - __asm__ __volatile__("movl %%gs:0, %0" : "=r" (__value)); + "movl %%gs:0, %0\n\t" #elif defined(__sparc) - register ulwp_t *__value __asm__("g7"); + ".register %%g7, #scratch\n\t" + "mov %%g7, %0\n\t" #else #error "port me" #endif + : "=r" (__value)); return (__value); } diff --git a/usr/src/lib/libc/inc/thr_uberdata.h b/usr/src/lib/libc/inc/thr_uberdata.h index c9e2670cc4..7f08a0b8c4 100644 --- a/usr/src/lib/libc/inc/thr_uberdata.h +++ b/usr/src/lib/libc/inc/thr_uberdata.h @@ -969,6 +969,7 @@ typedef struct uberdata { int ndaemons; /* total number of THR_DAEMON threads/lwps */ pid_t pid; /* the current process's pid */ void (*sigacthandler)(int, siginfo_t *, void *); + int (*setctxt)(const ucontext_t *); ulwp_t *lwp_stacks; ulwp_t *lwp_laststack; int nfreestack; @@ -981,6 +982,7 @@ typedef struct uberdata { robust_t **robustlocks; /* table of registered robust locks */ robust_t *robustlist; /* list of registered robust locks */ char *progname; /* the basename of the program, from argv[0] */ + char *ub_broot; /* the root of the native code in the brand */ void *ub_comm_page; /* arch-specific comm page of kernel data */ struct uberdata **tdb_bootstrap; tdb_t tdb; /* thread debug interfaces (for libc_db) */ @@ -1183,6 +1185,7 @@ typedef struct uberdata32 { int ndaemons; int pid; caddr32_t sigacthandler; + caddr32_t setctxt; caddr32_t lwp_stacks; caddr32_t lwp_laststack; int nfreestack; @@ -1195,6 +1198,7 @@ typedef struct uberdata32 { caddr32_t robustlocks; caddr32_t robustlist; caddr32_t progname; + caddr32_t ub_broot; caddr32_t ub_comm_page; caddr32_t tdb_bootstrap; tdb32_t tdb; @@ -1286,6 +1290,7 @@ extern void rwl_free(ulwp_t *); extern void heldlock_exit(void); extern void heldlock_free(ulwp_t *); extern void sigacthandler(int, siginfo_t *, void *); +extern int setctxt(const ucontext_t *); extern void signal_init(void); extern int sigequalset(const sigset_t *, const sigset_t *); extern void mutex_setup(void); diff --git a/usr/src/lib/libc/port/gen/arc4random.c b/usr/src/lib/libc/port/gen/arc4random.c index d82f12bd2a..f60ed09afc 100644 --- a/usr/src/lib/libc/port/gen/arc4random.c +++ b/usr/src/lib/libc/port/gen/arc4random.c @@ -2,7 +2,7 @@ * Copyright (c) 1996, David Mazieres <dm@uun.org> * Copyright (c) 2008, Damien Miller <djm@openbsd.org> * Copyright (c) 2013, Markus Friedl <markus@openbsd.org> - * Copyright (c) 2015 Joyent, Inc. + * Copyright 2019 Joyent, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -60,7 +60,7 @@ arc4_init(uint8_t *buf, size_t n) abort(); chacha_keysetup(&arc4->arc4_chacha, buf, ARC4_KEYSZ * 8, 0); - chacha_ivsetup(&arc4->arc4_chacha, buf + ARC4_KEYSZ); + chacha_ivsetup(&arc4->arc4_chacha, buf + ARC4_KEYSZ, NULL); } static void diff --git a/usr/src/lib/libc/port/gen/getauxv.c b/usr/src/lib/libc/port/gen/getauxv.c index 500675719c..4356a01392 100644 --- a/usr/src/lib/libc/port/gen/getauxv.c +++ b/usr/src/lib/libc/port/gen/getauxv.c @@ -24,9 +24,8 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "lint.h" +#include "thr_uberdata.h" #include <libc.h> #include <fcntl.h> #include <stdlib.h> @@ -38,6 +37,7 @@ #include <thread.h> #include <synch.h> #include <atomic.h> +#include <limits.h> static mutex_t auxlock = DEFAULTMUTEX; @@ -59,11 +59,20 @@ _getaux(int type) if (auxb == NULL) { lmutex_lock(&auxlock); if (auxb == NULL) { + uberdata_t *udp = curthread->ul_uberdata; struct stat statb; auxv_t *buf = NULL; + char *path = "/proc/self/auxv"; + char pbuf[PATH_MAX]; int fd; - if ((fd = open("/proc/self/auxv", O_RDONLY)) != -1 && + if (udp->ub_broot != NULL) { + (void) snprintf(pbuf, sizeof (pbuf), + "%s/proc/self/auxv", udp->ub_broot); + path = pbuf; + } + + if ((fd = open(path, O_RDONLY)) != -1 && fstat(fd, &statb) != -1) buf = libc_malloc( statb.st_size + sizeof (auxv_t)); diff --git a/usr/src/lib/libc/port/gen/sh_locks.c b/usr/src/lib/libc/port/gen/sh_locks.c index 6583efbc9c..cf879195c6 100644 --- a/usr/src/lib/libc/port/gen/sh_locks.c +++ b/usr/src/lib/libc/port/gen/sh_locks.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "lint.h" #include <mtlib.h> #include <sys/types.h> diff --git a/usr/src/lib/libc/port/gen/siglist.c b/usr/src/lib/libc/port/gen/siglist.c index 441cc4c2c5..bc6dc1b731 100644 --- a/usr/src/lib/libc/port/gen/siglist.c +++ b/usr/src/lib/libc/port/gen/siglist.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -128,6 +129,7 @@ static const char *_sys_siglist_data[NSIG] = { "Fourteenth Realtime Signal", /* SIGRTMIN+13 */ "Fifteenth Realtime Signal", /* SIGRTMIN+14 */ "Sixteenth Realtime Signal", /* SIGRTMIN+15 */ + "Seventeenth Realtime Signal", /* SIGRTMIN+16 */ "Sixteenth Last Realtime Signal", /* SIGRTMAX-15 */ "Fifteenth Last Realtime Signal", /* SIGRTMAX-14 */ "Fourteenth Last Realtime Signal", /* SIGRTMAX-13 */ diff --git a/usr/src/lib/libc/port/gen/str2sig.c b/usr/src/lib/libc/port/gen/str2sig.c index e0c4e89d68..02c6f3cb65 100644 --- a/usr/src/lib/libc/port/gen/str2sig.c +++ b/usr/src/lib/libc/port/gen/str2sig.c @@ -22,7 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright (c) 2014, Joyent, Inc. All rights reserved. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -102,6 +102,7 @@ static signame_t signames[] = { { "RTMIN+13", _SIGRTMIN+13 }, { "RTMIN+14", _SIGRTMIN+14 }, { "RTMIN+15", _SIGRTMIN+15 }, + { "RTMIN+16", _SIGRTMIN+16 }, { "RTMAX-15", _SIGRTMAX-15 }, { "RTMAX-14", _SIGRTMAX-14 }, { "RTMAX-13", _SIGRTMAX-13 }, diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index 4e24603312..da8de86f25 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -3057,6 +3057,10 @@ $endif __idmap_unreg; __init_daemon_priv; __init_suid_priv; + inotify_init; + inotify_init1; + inotify_add_watch; + inotify_rm_watch; _insert; inst_sync; _iswctype; @@ -3159,7 +3163,9 @@ $endif psecflags_validate; semctl64; _semctl64; + set_escaped_context_cleanup; set_setcontext_enforcement; + setcontext_sigmask; _setbufend; __set_errno; setprojrctl; @@ -3276,6 +3282,7 @@ $endif zone_list; zone_list_datalink; zonept; + zone_get_nroot; zone_remove_datalink; zone_setattr; zone_shutdown; diff --git a/usr/src/lib/libc/port/stdio/system.c b/usr/src/lib/libc/port/stdio/system.c index bc7e412d52..698d02b2ec 100644 --- a/usr/src/lib/libc/port/stdio/system.c +++ b/usr/src/lib/libc/port/stdio/system.c @@ -22,6 +22,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -43,6 +44,7 @@ #include <synch.h> #include <spawn.h> #include <paths.h> +#include <zone.h> #include "libc.h" extern const char **_environ; @@ -125,11 +127,18 @@ system(const char *cmd) int error; sigset_t mask; struct stat64 buf; - const char *shpath = _PATH_BSHELL; + char shpath[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); char *argv[4]; posix_spawnattr_t attr; static const char *shell = "sh"; + /* + * If executing in brand use native root. + */ + (void) snprintf(shpath, sizeof (shpath), "%s%s", + zroot != NULL ? zroot : "", _PATH_BSHELL); + if (cmd == NULL) { if (stat64(shpath, &buf) != 0) { return (0); diff --git a/usr/src/lib/libc/port/sys/epoll.c b/usr/src/lib/libc/port/sys/epoll.c index 34cb151135..e510b0b247 100644 --- a/usr/src/lib/libc/port/sys/epoll.c +++ b/usr/src/lib/libc/port/sys/epoll.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2016 Joyent, Inc. + * Copyright 2017 Joyent, Inc. */ #include <sys/types.h> @@ -64,6 +64,15 @@ #define EPOLLSWIZZLED \ (EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM) +/* + * The defined behavior for epoll_wait/epoll_pwait when using a timeout less + * than 0 is to wait for events until they arrive (or interrupted by a signal). + * While poll(7d) operates in this manner for a timeout of -1, using other + * negative values results in an immediate timeout, as if it had been set to 0. + * For that reason, negative values are clamped to -1. + */ +#define EPOLL_TIMEOUT_CLAMP(t) (((t) < -1) ? -1 : (t)) + int epoll_create(int size) { @@ -209,7 +218,7 @@ epoll_wait(int epfd, struct epoll_event *events, } arg.dp_nfds = maxevents; - arg.dp_timeout = timeout; + arg.dp_timeout = EPOLL_TIMEOUT_CLAMP(timeout); arg.dp_fds = (pollfd_t *)events; return (ioctl(epfd, DP_POLL, &arg)); @@ -227,7 +236,7 @@ epoll_pwait(int epfd, struct epoll_event *events, } arg.dp_nfds = maxevents; - arg.dp_timeout = timeout; + arg.dp_timeout = EPOLL_TIMEOUT_CLAMP(timeout); arg.dp_fds = (pollfd_t *)events; arg.dp_setp = (sigset_t *)sigmask; diff --git a/usr/src/lib/libc/port/sys/inotify.c b/usr/src/lib/libc/port/sys/inotify.c new file mode 100644 index 0000000000..90d04b5dd3 --- /dev/null +++ b/usr/src/lib/libc/port/sys/inotify.c @@ -0,0 +1,142 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ + +#include <sys/inotify.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <strings.h> +#include <dirent.h> + +int +inotify_init() +{ + return (open("/dev/inotify", O_RDWR)); +} + +int +inotify_init1(int flags) +{ + int oflags = O_RDWR; + + if (flags & IN_NONBLOCK) + oflags |= O_NONBLOCK; + + if (flags & IN_CLOEXEC) + oflags |= O_CLOEXEC; + + return (open("/dev/inotify", oflags)); +} + +int +inotify_add_watch(int fd, const char *pathname, uint32_t mask) +{ + inotify_addwatch_t ioc; + inotify_addchild_t cioc; + struct stat buf; + int dirfd, wd; + DIR *dir; + struct dirent *dp; + int oflags = O_RDONLY; + + if (mask & IN_DONT_FOLLOW) + oflags |= O_NOFOLLOW; + + if ((dirfd = open(pathname, oflags)) < 0) + return (-1); + + if (fstat(dirfd, &buf) != 0) { + (void) close(dirfd); + return (-1); + } + + if ((mask & IN_ONLYDIR) && !(buf.st_mode & S_IFDIR)) { + (void) close(dirfd); + errno = ENOTDIR; + return (-1); + } + + bzero(&ioc, sizeof (ioc)); + ioc.inaw_fd = dirfd; + ioc.inaw_mask = mask; + + if ((wd = ioctl(fd, INOTIFYIOC_ADD_WATCH, &ioc)) < 0) { + (void) close(dirfd); + return (-1); + } + + if (!(buf.st_mode & S_IFDIR) || !(mask & IN_CHILD_EVENTS)) { + (void) close(dirfd); + (void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd); + return (wd); + } + + /* + * If we have a directory and we have a mask that denotes child events, + * we need to manually add a child watch to every directory entry. + * (Because our watch is in place, it will automatically be added to + * files that are newly created after this point.) + */ + if ((dir = fdopendir(dirfd)) == NULL) { + (void) inotify_rm_watch(fd, wd); + (void) close(dirfd); + return (-1); + } + + bzero(&cioc, sizeof (cioc)); + cioc.inac_fd = dirfd; + + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, ".") == 0) + continue; + + if (strcmp(dp->d_name, "..") == 0) + continue; + + cioc.inac_name = dp->d_name; + + if (ioctl(fd, INOTIFYIOC_ADD_CHILD, &cioc) != 0) { + /* + * If we get an error that indicates clear internal + * malfunctioning, we propagate the error. Otherwise + * we eat it: this could be a file that no longer + * exists or a symlink or something else that we + * can't lookup. + */ + switch (errno) { + case ENXIO: + case EFAULT: + case EBADF: + (void) closedir(dir); + (void) inotify_rm_watch(fd, wd); + return (-1); + default: + break; + } + } + } + + (void) closedir(dir); + (void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd); + + return (wd); +} + +int +inotify_rm_watch(int fd, int wd) +{ + return (ioctl(fd, INOTIFYIOC_RM_WATCH, wd)); +} diff --git a/usr/src/lib/libc/port/sys/time_util.c b/usr/src/lib/libc/port/sys/time_util.c index 93a07aaee0..b6ea0291e2 100644 --- a/usr/src/lib/libc/port/sys/time_util.c +++ b/usr/src/lib/libc/port/sys/time_util.c @@ -22,10 +22,9 @@ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <time.h> #include <errno.h> @@ -39,6 +38,10 @@ void hrt2ts(hrtime_t hrt, timespec_t *tsp) { +#if defined(__amd64) + tsp->tv_sec = hrt / NANOSEC; + tsp->tv_nsec = hrt % NANOSEC; +#else uint32_t sec, nsec, tmp; tmp = (uint32_t)(hrt >> 30); @@ -60,6 +63,7 @@ hrt2ts(hrtime_t hrt, timespec_t *tsp) } tsp->tv_sec = (time_t)sec; tsp->tv_nsec = nsec; +#endif /* defined(__amd64) */ } /* @@ -67,8 +71,8 @@ hrt2ts(hrtime_t hrt, timespec_t *tsp) * All *timedwait() system call traps expect relative time. */ void -abstime_to_reltime(clockid_t clock_id, - const timespec_t *abstime, timespec_t *reltime) +abstime_to_reltime(clockid_t clock_id, const timespec_t *abstime, + timespec_t *reltime) { extern int __clock_gettime(clockid_t, timespec_t *); timespec_t now; diff --git a/usr/src/lib/libc/port/sys/zone.c b/usr/src/lib/libc/port/sys/zone.c index 4a4c70043d..8cf28c3ccf 100644 --- a/usr/src/lib/libc/port/sys/zone.c +++ b/usr/src/lib/libc/port/sys/zone.c @@ -22,9 +22,11 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent Inc. All rights reserved. */ #include "lint.h" +#include "thr_uberdata.h" #include <sys/types.h> #include <sys/syscall.h> #include <sys/zone.h> @@ -39,7 +41,8 @@ zoneid_t zone_create(const char *name, const char *root, const struct priv_set *privs, const char *rctls, size_t rctlsz, const char *zfs, size_t zfssz, - int *extended_error, int match, int doi, const bslabel_t *label, int flags) + int *extended_error, int match, int doi, const bslabel_t *label, int flags, + zoneid_t req_zoneid) { zone_def zd; priv_data_t *d; @@ -59,6 +62,7 @@ zone_create(const char *name, const char *root, const struct priv_set *privs, zd.doi = doi; zd.label = label; zd.flags = flags; + zd.zoneid = req_zoneid; return ((zoneid_t)syscall(SYS_zone, ZONE_CREATE, &zd)); } @@ -241,3 +245,10 @@ zone_list_datalink(zoneid_t zoneid, int *dlnump, datalink_id_t *linkids) { return (syscall(SYS_zone, ZONE_LIST_DATALINK, zoneid, dlnump, linkids)); } + +const char * +zone_get_nroot() +{ + uberdata_t *udp = curthread->ul_uberdata; + return (udp->ub_broot); +} diff --git a/usr/src/lib/libc/port/threads/sigaction.c b/usr/src/lib/libc/port/threads/sigaction.c index 571e211f97..6a283be33b 100644 --- a/usr/src/lib/libc/port/threads/sigaction.c +++ b/usr/src/lib/libc/port/threads/sigaction.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. */ #include "lint.h" @@ -284,6 +285,24 @@ take_deferred_signal(int sig) thr_panic("take_deferred_signal(): __sigresend() failed"); } +/* + * sigacthandler() attempts to clean up dangling uc_link pointers in + * signal handling contexts when libc believes us to have escaped + * a signal handler incorrectly in the past. + * + * Branded processes have a legitimate use for a chain including contexts + * other than those used for signal handling when tracking emulation + * requests from the kernel. We allow them to disable this cleanup + * behaviour. + */ +static int escaped_context_cleanup = 1; + +void +set_escaped_context_cleanup(int on) +{ + escaped_context_cleanup = on; +} + void sigacthandler(int sig, siginfo_t *sip, void *uvp) { @@ -306,7 +325,7 @@ sigacthandler(int sig, siginfo_t *sip, void *uvp) * we are actually executing at main level (self->ul_siglink == NULL). * See the code for setjmp()/longjmp() for more details. */ - if (self->ul_siglink == NULL) + if (escaped_context_cleanup && self->ul_siglink == NULL) ucp->uc_link = NULL; /* @@ -458,11 +477,12 @@ sigaction(int sig, const struct sigaction *nact, struct sigaction *oact) } /* - * This is a private interface for the linux brand interface. + * This is a private interface for the lx brand. */ void setsigacthandler(void (*nsigacthandler)(int, siginfo_t *, void *), - void (**osigacthandler)(int, siginfo_t *, void *)) + void (**osigacthandler)(int, siginfo_t *, void *), + int (*brsetctxt)(const ucontext_t *)) { ulwp_t *self = curthread; uberdata_t *udp = self->ul_uberdata; @@ -471,6 +491,9 @@ setsigacthandler(void (*nsigacthandler)(int, siginfo_t *, void *), *osigacthandler = udp->sigacthandler; udp->sigacthandler = nsigacthandler; + + if (brsetctxt != NULL) + udp->setctxt = brsetctxt; } /* @@ -517,11 +540,39 @@ set_setcontext_enforcement(int on) setcontext_enforcement = on; } +/* + * The LX brand emulation library implements an operation that is analogous to + * setcontext(), but takes a different path in to the kernel. So that it can + * correctly restore a signal mask, we expose just the signal mask handling + * part of the regular setcontext() routine as a private interface. + */ +void +setcontext_sigmask(ucontext_t *ucp) +{ + ulwp_t *self = curthread; + + if (ucp->uc_flags & UC_SIGMASK) { + block_all_signals(self); + delete_reserved_signals(&ucp->uc_sigmask); + self->ul_sigmask = ucp->uc_sigmask; + if (self->ul_cursig) { + /* + * We have a deferred signal present. + * The signal mask will be set when the + * signal is taken in take_deferred_signal(). + */ + ASSERT(self->ul_critical + self->ul_sigdefer != 0); + ucp->uc_flags &= ~UC_SIGMASK; + } + } +} + #pragma weak _setcontext = setcontext int setcontext(const ucontext_t *ucp) { ulwp_t *self = curthread; + uberdata_t *udp = self->ul_uberdata; int ret; ucontext_t uc; @@ -536,20 +587,7 @@ setcontext(const ucontext_t *ucp) /* * Restore previous signal mask and context link. */ - if (uc.uc_flags & UC_SIGMASK) { - block_all_signals(self); - delete_reserved_signals(&uc.uc_sigmask); - self->ul_sigmask = uc.uc_sigmask; - if (self->ul_cursig) { - /* - * We have a deferred signal present. - * The signal mask will be set when the - * signal is taken in take_deferred_signal(). - */ - ASSERT(self->ul_critical + self->ul_sigdefer != 0); - uc.uc_flags &= ~UC_SIGMASK; - } - } + setcontext_sigmask(&uc); self->ul_siglink = uc.uc_link; /* @@ -578,7 +616,7 @@ setcontext(const ucontext_t *ucp) */ set_parking_flag(self, 0); self->ul_sp = 0; - ret = __setcontext(&uc); + ret = udp->setctxt(&uc); /* * It is OK for setcontext() to return if the user has not specified diff --git a/usr/src/lib/libc/port/threads/thr.c b/usr/src/lib/libc/port/threads/thr.c index 2a982eb8c8..8026ffad9c 100644 --- a/usr/src/lib/libc/port/threads/thr.c +++ b/usr/src/lib/libc/port/threads/thr.c @@ -127,6 +127,7 @@ uberdata_t __uberdata = { 0, /* ndaemons */ 0, /* pid */ sigacthandler, /* sigacthandler */ + __setcontext, /* setctxt */ NULL, /* lwp_stacks */ NULL, /* lwp_laststack */ 0, /* nfreestack */ @@ -139,6 +140,7 @@ uberdata_t __uberdata = { NULL, /* robustlocks */ NULL, /* robustlist */ NULL, /* progname */ + NULL, /* ub_broot */ NULL, /* ub_comm_page */ NULL, /* __tdb_bootstrap */ { /* tdb */ @@ -1237,13 +1239,19 @@ init_auxv_data(uberdata_t *udp) { Dl_argsinfo_t args; + udp->ub_broot = NULL; udp->ub_comm_page = NULL; if (dlinfo(RTLD_SELF, RTLD_DI_ARGSINFO, &args) < 0) return; while (args.dla_auxv->a_type != AT_NULL) { - if (args.dla_auxv->a_type == AT_SUN_COMMPAGE) { + switch (args.dla_auxv->a_type) { + case AT_SUN_BRAND_NROOT: + udp->ub_broot = args.dla_auxv->a_un.a_ptr; + break; + case AT_SUN_COMMPAGE: udp->ub_comm_page = args.dla_auxv->a_un.a_ptr; + break; } args.dla_auxv++; } @@ -1287,7 +1295,8 @@ libc_init(void) /* * Every libc, regardless of link map, needs to go through and check * its aux vectors. Doing so will indicate whether or not this has - * been given a comm page (to optimize certain system actions). + * been given a brand root (used to qualify various other data) or a + * comm page (to optimize certain system actions). */ init_auxv_data(udp); diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com index 3fb4e21fe4..ee580e8513 100644 --- a/usr/src/lib/libc/sparc/Makefile.com +++ b/usr/src/lib/libc/sparc/Makefile.com @@ -21,10 +21,10 @@ # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2019 Joyent, Inc. # Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. # Copyright 2013 Garrett D'Amore <garrett@damore.org> # Copyright 2018 Nexenta Systems, Inc. +# Copyright 2019 Joyent, Inc. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # Copyright 2019 Peter Tribble. # @@ -972,6 +972,7 @@ PORTSYS= \ fcntl.o \ getpagesizes.o \ getpeerucred.o \ + inotify.o \ inst_sync.o \ issetugid.o \ label.o \ diff --git a/usr/src/lib/libc/sparc/gen/siginfolst.c b/usr/src/lib/libc/sparc/gen/siginfolst.c index 8451dfbb4f..8b8a1b4669 100644 --- a/usr/src/lib/libc/sparc/gen/siginfolst.c +++ b/usr/src/lib/libc/sparc/gen/siginfolst.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = { 0, 0, 0, 0, 0, 0, /* SIGRTMIN+15 */ + 0, 0, /* SIGRTMIN+16 */ 0, 0, /* SIGRTMAX-15 */ 0, 0, 0, 0, diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com index afe2c70fa7..3aacce8f61 100644 --- a/usr/src/lib/libc/sparcv9/Makefile.com +++ b/usr/src/lib/libc/sparcv9/Makefile.com @@ -22,10 +22,10 @@ # # Copyright 2016 Gary Mills # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2019 Joyent, Inc. # Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. # Copyright 2013 Garrett D'Amore <garrett@damore.org> # Copyright 2018 Nexenta Systems, Inc. +# Copyright 2019 Joyent, Inc. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # Copyright 2019 Peter Tribble. # @@ -908,6 +908,7 @@ PORTSYS= \ chmod.o \ chown.o \ corectl.o \ + epoll.o \ eventfd.o \ epoll.o \ exacctsys.o \ diff --git a/usr/src/lib/libc/sparcv9/gen/siginfolst.c b/usr/src/lib/libc/sparcv9/gen/siginfolst.c index 8451dfbb4f..8b8a1b4669 100644 --- a/usr/src/lib/libc/sparcv9/gen/siginfolst.c +++ b/usr/src/lib/libc/sparcv9/gen/siginfolst.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = { 0, 0, 0, 0, 0, 0, /* SIGRTMIN+15 */ + 0, 0, /* SIGRTMIN+16 */ 0, 0, /* SIGRTMAX-15 */ 0, 0, 0, 0, diff --git a/usr/src/lib/libctf/common/ctf_convert.c b/usr/src/lib/libctf/common/ctf_convert.c index 06f4135593..9441aa6ed7 100644 --- a/usr/src/lib/libctf/common/ctf_convert.c +++ b/usr/src/lib/libctf/common/ctf_convert.c @@ -99,7 +99,7 @@ ctf_has_c_source(Elf *elf, char *errmsg, size_t errlen) return (ret); } -static ctf_file_t * +ctf_file_t * ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags, int *errp, char *errbuf, size_t errlen) { diff --git a/usr/src/lib/libctf/common/ctf_lib.c b/usr/src/lib/libctf/common/ctf_lib.c index 4cb5ce0ac5..5177409200 100644 --- a/usr/src/lib/libctf/common/ctf_lib.c +++ b/usr/src/lib/libctf/common/ctf_lib.c @@ -37,6 +37,7 @@ #include <dlfcn.h> #include <gelf.h> #include <zlib.h> +#include <zone.h> #include <sys/debug.h> #ifdef _LP64 @@ -92,15 +93,36 @@ _libctf_init(void) void * ctf_zopen(int *errp) { - ctf_dprintf("decompressing CTF data using %s\n", _libctf_zlib); + char buf[MAXPATHLEN]; + const char *path = _libctf_zlib, *zroot; if (zlib.z_dlp != NULL) return (zlib.z_dlp); /* library is already loaded */ - if (access(_libctf_zlib, R_OK) == -1) + /* + * Get the zone native root. For the tools build, we don't need + * this (it seems fair to impose that we always build the system in + * a native zone), and we want to allow build machines that are older + * that the notion of the native root, so we only actually make this + * call if we're not the tools build. + */ +#ifndef CTF_TOOLS_BUILD + zroot = zone_get_nroot(); +#else + zroot = NULL; +#endif + + if (zroot != NULL) { + (void) snprintf(buf, MAXPATHLEN, "%s/%s", zroot, _libctf_zlib); + path = buf; + } + + ctf_dprintf("decompressing CTF data using %s\n", path); + + if (access(path, R_OK) == -1) return (ctf_set_open_errno(errp, ECTF_ZMISSING)); - if ((zlib.z_dlp = dlopen(_libctf_zlib, RTLD_LAZY | RTLD_LOCAL)) == NULL) + if ((zlib.z_dlp = dlopen(path, RTLD_LAZY | RTLD_LOCAL)) == NULL) return (ctf_set_open_errno(errp, ECTF_ZINIT)); zlib.z_uncompress = (int (*)()) dlsym(zlib.z_dlp, "uncompress"); diff --git a/usr/src/lib/libctf/common/libctf.h b/usr/src/lib/libctf/common/libctf.h index 57b8376eed..78b0a7a786 100644 --- a/usr/src/lib/libctf/common/libctf.h +++ b/usr/src/lib/libctf/common/libctf.h @@ -81,6 +81,8 @@ extern void ctf_diff_fini(ctf_diff_t *); */ #define CTF_ALLOW_MISSING_DEBUG 0x01 +extern ctf_file_t *ctf_elfconvert(int, Elf *, const char *, uint_t, uint_t, + int *, char *, size_t); extern ctf_file_t *ctf_fdconvert(int, const char *, uint_t, uint_t, int *, char *, size_t); diff --git a/usr/src/lib/libctf/common/mapfile-vers b/usr/src/lib/libctf/common/mapfile-vers index 37ef440ab3..9281bbfff5 100644 --- a/usr/src/lib/libctf/common/mapfile-vers +++ b/usr/src/lib/libctf/common/mapfile-vers @@ -78,6 +78,7 @@ SYMBOL_VERSION SUNWprivate_1.2 { ctf_diff_types; ctf_discard; ctf_dup; + ctf_elfconvert; ctf_elffdwrite; ctf_elfwrite; ctf_enum_value; diff --git a/usr/src/lib/libcurses/screen/setupterm.c b/usr/src/lib/libcurses/screen/setupterm.c index c1f4bdeb04..f67e8751b7 100644 --- a/usr/src/lib/libcurses/screen/setupterm.c +++ b/usr/src/lib/libcurses/screen/setupterm.c @@ -37,8 +37,6 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /*LINTLIBRARY*/ #include <stdio.h> @@ -48,6 +46,7 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <zone.h> #include "curses_inc.h" #define TERMPATH "/usr/share/lib/terminfo/" @@ -272,9 +271,11 @@ setupterm(char *term, int filenum, int *errret) } if (tfd < 0) { + const char *zroot = zone_get_nroot(); /* /usr/share/lib/terminfo/?/$TERM */ if (snprintf(fname, sizeof (fname), - "%s/%c/%s", TERMPATH, *term, term) >= sizeof (fname)) { + "%s/%s/%c/%s", zroot == NULL ? "" : zroot, TERMPATH, + *term, term) >= sizeof (fname)) { term_errno = TERMINFO_TOO_LONG; goto out_err; } diff --git a/usr/src/lib/libdevid/Makefile.com b/usr/src/lib/libdevid/Makefile.com index f0e6af9ee5..7b69bdf822 100644 --- a/usr/src/lib/libdevid/Makefile.com +++ b/usr/src/lib/libdevid/Makefile.com @@ -22,6 +22,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2018 Joyent, Inc. +# LIBRARY= libdevid.a VERS= .1 OBJECTS= deviceid.o devid.o devid_scsi.o devid_smp.o diff --git a/usr/src/lib/libdhcpagent/common/dhcpagent_util.c b/usr/src/lib/libdhcpagent/common/dhcpagent_util.c index 1c381fa08f..7a44ae5249 100644 --- a/usr/src/lib/libdhcpagent/common/dhcpagent_util.c +++ b/usr/src/lib/libdhcpagent/common/dhcpagent_util.c @@ -36,6 +36,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <zone.h> #include "dhcpagent_ipc.h" #include "dhcpagent_util.h" @@ -125,6 +126,12 @@ dhcp_start_agent(int timeout) int ctfd; pid_t childpid; ctid_t ct; + char dhcpcmd[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); + + /* Prepend the root of the native code in the brand to the command */ + (void) snprintf(dhcpcmd, sizeof (dhcpcmd), "%s%s", zroot != NULL ? + zroot : "", DHCP_AGENT_PATH); /* * just send a dummy request to the agent to find out if it's @@ -160,7 +167,7 @@ dhcp_start_agent(int timeout) goto fail; case 0: - (void) execl(DHCP_AGENT_PATH, DHCP_AGENT_PATH, (char *)0); + (void) execl(dhcpcmd, dhcpcmd, (char *)0); _exit(EXIT_FAILURE); default: diff --git a/usr/src/lib/libdiskmgt/common/findevs.c b/usr/src/lib/libdiskmgt/common/findevs.c index 8114f31352..2ac0e6cada 100644 --- a/usr/src/lib/libdiskmgt/common/findevs.c +++ b/usr/src/lib/libdiskmgt/common/findevs.c @@ -27,6 +27,7 @@ /* * Copyright (c) 2011 by Delphix. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. + * Copyright 2017 Joyent, Inc. */ #include <fcntl.h> @@ -67,6 +68,7 @@ static char *ctrltypes[] = { DDI_NT_FC_ATTACHMENT_POINT, DDI_NT_NVME_ATTACHMENT_POINT, + DDI_NT_NVME_NEXUS, DDI_NT_SATA_ATTACHMENT_POINT, DDI_NT_SATA_NEXUS, DDI_NT_SCSI_ATTACHMENT_POINT, @@ -1019,8 +1021,10 @@ ctype(di_node_t node, di_minor_t minor) libdiskmgt_str_eq(name, "fp"))) return (DM_CTYPE_FIBRE); - if (libdiskmgt_str_eq(type, DDI_NT_NVME_ATTACHMENT_POINT)) + if (libdiskmgt_str_eq(type, DDI_NT_NVME_NEXUS) || + libdiskmgt_str_eq(type, DDI_NT_NVME_ATTACHMENT_POINT)) { return (DM_CTYPE_NVME); + } if (libdiskmgt_str_eq(type, DDI_NT_SATA_NEXUS) || libdiskmgt_str_eq(type, DDI_NT_SATA_ATTACHMENT_POINT)) diff --git a/usr/src/lib/libdiskmgt/common/libdiskmgt.h b/usr/src/lib/libdiskmgt/common/libdiskmgt.h index 7391350962..96bf472842 100644 --- a/usr/src/lib/libdiskmgt/common/libdiskmgt.h +++ b/usr/src/lib/libdiskmgt/common/libdiskmgt.h @@ -25,8 +25,8 @@ */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. + * Copyright (c) 2017, Joyent, Inc. All rights reserved. */ #ifndef _LIBDISKMGT_H diff --git a/usr/src/lib/libdladm/Makefile b/usr/src/lib/libdladm/Makefile index dcec6e0ceb..dc61aabd00 100644 --- a/usr/src/lib/libdladm/Makefile +++ b/usr/src/lib/libdladm/Makefile @@ -20,6 +20,7 @@ # # # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015, Joyent, Inc. # # @@ -29,7 +30,7 @@ HDRS = libdladm.h libdladm_impl.h libdllink.h libdlaggr.h \ libdlwlan.h libdlwlan_impl.h libdlvnic.h libdlvlan.h \ libdlmgmt.h libdlflow.h libdlflow_impl.h libdlstat.h \ libdlether.h libdlsim.h libdlbridge.h libdliptun.h \ - libdlib.h + libdlib.h libdloverlay.h HDRDIR = common @@ -50,6 +51,15 @@ MSGFILES = common/libdladm.c common/linkprop.c common/secobj.c \ XGETFLAGS = -a -x libdladm.xcl +TYPECHECK_LIB = libdladm.so.1 +TYPELIST = overlay_ioc_create_t \ + overlay_ioc_activate_t \ + overlay_ioc_delete_t \ + overlay_ioc_nprops_t \ + overlay_ioc_propinfo_t \ + overlay_ioc_prop_t \ + mac_protect_t + all := TARGET = all clean := TARGET = clean clobber := TARGET = clobber @@ -61,7 +71,7 @@ all clean clobber install: $(SUBDIRS) install_h: $(ROOTHDRS) -check: $(CHECKHDRS) +check: $(CHECKHDRS) $(TYPECHECK) $(POFILE): pofile_MSGFILES diff --git a/usr/src/lib/libdladm/Makefile.com b/usr/src/lib/libdladm/Makefile.com index d170a97998..19db4ccace 100644 --- a/usr/src/lib/libdladm/Makefile.com +++ b/usr/src/lib/libdladm/Makefile.com @@ -20,7 +20,7 @@ # # # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2020 Joyent, Inc. # LIBRARY = libdladm.a @@ -28,7 +28,8 @@ VERS = .1 OBJECTS = libdladm.o secobj.o linkprop.o libdllink.o libdlaggr.o \ libdlwlan.o libdlvnic.o libdlmgmt.o libdlvlan.o libdlib.o\ flowattr.o flowprop.o propfuncs.o libdlflow.o libdlstat.o \ - usage.o libdlether.o libdlsim.o libdlbridge.o libdliptun.o + usage.o libdlether.o libdlsim.o libdlbridge.o libdliptun.o \ + libdloverlay.o include ../../Makefile.lib @@ -37,7 +38,7 @@ include ../../Makefile.rootfs LIBS = $(DYNLIB) LDLIBS += -ldevinfo -lc -linetutil -lsocket -lscf -lrcm -lnvpair \ - -lexacct -lkstat -lpool + -lexacct -lkstat -lpool -lvarpd SRCDIR = ../common diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c index eb099376a4..446a15893b 100644 --- a/usr/src/lib/libdladm/common/libdladm.c +++ b/usr/src/lib/libdladm/common/libdladm.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017, Joyent, Inc. */ /* @@ -37,6 +38,9 @@ #include <strings.h> #include <dirent.h> #include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/param.h> @@ -103,6 +107,18 @@ static link_protect_t link_protect_types[] = { }; #define LPTYPES (sizeof (link_protect_types) / sizeof (link_protect_t)) +typedef struct { + uint32_t ld_type; + char *ld_name; +} link_dynamic_t; + +static link_dynamic_t link_dynamic_types[] = { + { MPT_DYN_DHCPV4, "dhcpv4" }, + { MPT_DYN_DHCPV6, "dhcpv6" }, + { MPT_DYN_SLAAC, "slaac" }, +}; +#define DYNTYPES (sizeof (link_dynamic_types) / sizeof (link_dynamic_t)) + dladm_status_t dladm_open(dladm_handle_t *handle) { @@ -437,6 +453,9 @@ dladm_status2str(dladm_status_t status, char *buf) case DLADM_STATUS_INVALID_MTU: s = "MTU check failed, MTU outside of device's supported range"; break; + case DLADM_STATUS_BAD_ENCAP: + s = "invalid encapsulation protocol"; + break; case DLADM_STATUS_PERSIST_ON_TEMP: s = "can't create persistent object on top of temporary object"; break; @@ -672,6 +691,9 @@ dladm_class2str(datalink_class_t class, char *buf) case DATALINK_CLASS_PART: s = "part"; break; + case DATALINK_CLASS_OVERLAY: + s = "overlay"; + break; default: s = "unknown"; break; @@ -959,6 +981,28 @@ dladm_protect2str(uint32_t ptype, char *buf) return (buf); } + +/* + * Convert dynamic address method value to a string. + */ +const char * +dladm_dynamic2str(uint32_t dtype, char *buf, size_t size) +{ + const char *s = "--"; + link_dynamic_t *ld; + int i; + + for (i = 0; i < DYNTYPES; i++) { + ld = &link_dynamic_types[i]; + if (ld->ld_type == dtype) { + s = ld->ld_name; + break; + } + } + (void) snprintf(buf, size, "%s", dgettext(TEXT_DOMAIN, s)); + return (buf); +} + /* * Convert an IPv4 address to/from a string. */ @@ -1100,8 +1144,8 @@ fail: * is allocated here but should be freed by the caller. */ dladm_status_t -dladm_strs2range(char **prop_val, uint_t val_cnt, mac_propval_type_t type, - mac_propval_range_t **range) +dladm_strs2range(char **prop_val, uint_t val_cnt, + mac_propval_type_t type, mac_propval_range_t **range) { int i; char *endp; @@ -1157,15 +1201,15 @@ dladm_strs2range(char **prop_val, uint_t val_cnt, mac_propval_type_t type, * Convert a mac_propval_range_t structure into an array of elements. */ dladm_status_t -dladm_range2list(mac_propval_range_t *rangep, void *elem, uint_t *nelem) +dladm_range2list(const mac_propval_range_t *rangep, void *elem, uint_t *nelem) { int i, j, k; dladm_status_t status = DLADM_STATUS_OK; switch (rangep->mpr_type) { case MAC_PROPVAL_UINT32: { - mac_propval_uint32_range_t *ur; - uint32_t *elem32 = elem; + const mac_propval_uint32_range_t *ur; + uint32_t *elem32 = elem; k = 0; ur = &rangep->mpr_range_uint32[0]; @@ -1193,13 +1237,13 @@ dladm_range2list(mac_propval_range_t *rangep, void *elem, uint_t *nelem) * of single elements or ranges. */ int -dladm_range2strs(mac_propval_range_t *rangep, char **prop_val) +dladm_range2strs(const mac_propval_range_t *rangep, char **prop_val) { int i; switch (rangep->mpr_type) { case MAC_PROPVAL_UINT32: { - mac_propval_uint32_range_t *ur; + const mac_propval_uint32_range_t *ur; /* Write ranges and individual elements */ ur = &rangep->mpr_range_uint32[0]; @@ -1216,6 +1260,20 @@ dladm_range2strs(mac_propval_range_t *rangep, char **prop_val) } return (0); } + case MAC_PROPVAL_STR: { + const mac_propval_str_range_t *str; + size_t coff, len; + + coff = 0; + str = &rangep->u.mpr_str; + for (i = 0; i < rangep->mpr_count; i++) { + len = strlen(&str->mpur_data[coff]); + (void) strlcpy(prop_val[i], &str->mpur_data[coff], + DLADM_PROP_VAL_MAX); + coff += len + 1; + } + return (0); + } default: break; } @@ -1293,3 +1351,54 @@ dladm_list2range(void *elem, uint_t nelem, mac_propval_type_t type, return (status); } + +void +dladm_errlist_init(dladm_errlist_t *erl) +{ + bzero(erl, sizeof (dladm_errlist_t)); +} + +void +dladm_errlist_reset(dladm_errlist_t *erl) +{ + uint_t i; + + for (i = 0; i < erl->el_count; i++) + free(erl->el_errs[i]); + free(erl->el_errs); + dladm_errlist_init(erl); +} + +dladm_status_t +dladm_errlist_append(dladm_errlist_t *erl, const char *fmt, ...) +{ + int ret; + va_list ap; + char *m = NULL; + + if (erl->el_count == erl->el_alloc) { + int alloc; + void *addr; + if (erl->el_alloc == 0) { + assert(erl->el_errs == NULL); + alloc = 32; + } else { + alloc = erl->el_alloc + 32; + } + addr = realloc(erl->el_errs, sizeof (char *) * alloc); + if (addr == NULL) + return (DLADM_STATUS_NOMEM); + + erl->el_errs = addr; + erl->el_alloc = alloc; + } + + va_start(ap, fmt); + ret = vasprintf(&m, fmt, ap); + va_end(ap); + if (ret == -1) + return (dladm_errno2status(errno)); + erl->el_errs[erl->el_count] = m; + erl->el_count++; + return (DLADM_STATUS_OK); +} diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h index 350c9c50f3..685356fa64 100644 --- a/usr/src/lib/libdladm/common/libdladm.h +++ b/usr/src/lib/libdladm/common/libdladm.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017, Joyent, Inc. */ /* @@ -46,7 +47,7 @@ extern "C" { #endif #define LINKID_STR_WIDTH 10 -#define DLADM_STRSIZE 256 +#define DLADM_STRSIZE 2048 /* * option flags taken by the libdladm functions @@ -76,6 +77,10 @@ extern "C" { * - DLADM_OPT_BOOT: * Bypass check functions during boot (used by pool property since pools * can come up after link properties are set) + * + * - DLADM_OPT_TRANSIENT: + * Indicates that the link assigned to a zone is transient and will be + * removed when the zone shuts down. */ #define DLADM_OPT_ACTIVE 0x00000001 #define DLADM_OPT_PERSIST 0x00000002 @@ -86,6 +91,7 @@ extern "C" { #define DLADM_OPT_VLAN 0x00000040 #define DLADM_OPT_NOREFRESH 0x00000080 #define DLADM_OPT_BOOT 0x00000100 +#define DLADM_OPT_TRANSIENT 0x00000200 #define DLADM_WALK_TERMINATE 0 #define DLADM_WALK_CONTINUE -1 @@ -179,6 +185,7 @@ typedef enum { DLADM_STATUS_INVALID_PKEY_TBL_SIZE, DLADM_STATUS_PORT_NOPROTO, DLADM_STATUS_INVALID_MTU, + DLADM_STATUS_BAD_ENCAP, DLADM_STATUS_PERSIST_ON_TEMP } dladm_status_t; @@ -233,6 +240,12 @@ typedef struct dladm_arg_list { char *al_buf; } dladm_arg_list_t; +typedef struct dladm_errlist { + uint_t el_count; + uint_t el_alloc; + char **el_errs; +} dladm_errlist_t; + typedef enum { DLADM_LOGTYPE_LINK = 1, DLADM_LOGTYPE_FLOW @@ -264,6 +277,7 @@ extern dladm_status_t dladm_str2pri(char *, mac_priority_level_t *); extern const char *dladm_pri2str(mac_priority_level_t, char *); extern dladm_status_t dladm_str2protect(char *, uint32_t *); extern const char *dladm_protect2str(uint32_t, char *); +extern const char *dladm_dynamic2str(uint32_t, char *, size_t); extern dladm_status_t dladm_str2ipv4addr(char *, void *); extern const char *dladm_ipv4addr2str(void *, char *); extern dladm_status_t dladm_str2ipv6addr(char *, void *); @@ -294,12 +308,15 @@ extern dladm_status_t dladm_zone_halt(dladm_handle_t, zoneid_t); extern dladm_status_t dladm_strs2range(char **, uint_t, mac_propval_type_t, mac_propval_range_t **); -extern dladm_status_t dladm_range2list(mac_propval_range_t *, void*, +extern dladm_status_t dladm_range2list(const mac_propval_range_t *, void *, uint_t *); -extern int dladm_range2strs(mac_propval_range_t *, char **); +extern int dladm_range2strs(const mac_propval_range_t *, char **); extern dladm_status_t dladm_list2range(void *, uint_t, mac_propval_type_t, mac_propval_range_t **); +extern void dladm_errlist_init(dladm_errlist_t *); +extern void dladm_errlist_reset(dladm_errlist_t *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libdladm/common/libdladm_impl.h b/usr/src/lib/libdladm/common/libdladm_impl.h index 20db1cb1d7..ce4b6dc81b 100644 --- a/usr/src/lib/libdladm/common/libdladm_impl.h +++ b/usr/src/lib/libdladm/common/libdladm_impl.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017, Joyent, Inc. */ /* @@ -151,10 +152,10 @@ extern dladm_status_t dladm_flow_proplist_extract(dladm_arg_list_t *, * by the pd_check function. */ typedef dladm_status_t rp_extractf_t(val_desc_t *, uint_t, void *); -extern rp_extractf_t extract_maxbw, extract_priority, - extract_cpus, extract_protection, - extract_allowedips, extract_allowedcids, - extract_rxrings, extract_txrings, extract_pool; +extern rp_extractf_t extract_dynamic_methods, extract_priority, extract_cpus, + extract_protection, extract_allowallcids, extract_pool, + extract_allowedips, extract_allowedcids, extract_maxbw, + extract_rxrings, extract_txrings; typedef struct resource_prop_s { /* @@ -173,6 +174,12 @@ typedef struct resource_prop_s { */ #define FBRIDGE "bridge" /* string */ +/* + * For error lists + */ +extern dladm_status_t dladm_errlist_append(dladm_errlist_t *, + const char *, ...); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libdladm/common/libdllink.c b/usr/src/lib/libdladm/common/libdllink.c index 58bec0ad30..7e952b97e9 100644 --- a/usr/src/lib/libdladm/common/libdllink.c +++ b/usr/src/lib/libdladm/common/libdllink.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #include <sys/types.h> @@ -386,10 +387,14 @@ dladm_linkduplex2str(link_duplex_t duplex, char *buf) /* * Case 1: rename an existing link1 to a link2 that does not exist. * Result: <linkid1, link2> + * The zonename parameter is used to allow us to create a VNIC in the global + * zone which is assigned to a non-global zone. Since there is a race condition + * in the create process if two VNICs have the same name, we need to rename it + * after it has been assigned to the zone. */ static dladm_status_t i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1, - const char *link1, const char *link2, uint32_t flags) + const char *link1, const char *link2, uint32_t flags, const char *zonename) { dld_ioc_rename_t dir; dladm_status_t status = DLADM_STATUS_OK; @@ -402,6 +407,10 @@ i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1, dir.dir_linkid1 = linkid1; dir.dir_linkid2 = DATALINK_INVALID_LINKID; (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN); + if (zonename != NULL) + dir.dir_zoneinit = B_TRUE; + else + dir.dir_zoneinit = B_FALSE; if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) { status = dladm_errno2status(errno); @@ -412,6 +421,7 @@ i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1, status = dladm_remap_datalink_id(handle, linkid1, link2); if (status != DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) { (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN); + dir.dir_zoneinit = B_FALSE; (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir); } return (status); @@ -508,6 +518,7 @@ i_dladm_rename_link_c2(dladm_handle_t handle, datalink_id_t linkid1, */ dir.dir_linkid1 = linkid1; dir.dir_linkid2 = linkid2; + dir.dir_zoneinit = B_FALSE; if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) status = dladm_errno2status(errno); @@ -616,7 +627,8 @@ done: } dladm_status_t -dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2) +dladm_rename_link(dladm_handle_t handle, const char *zonename, + const char *link1, const char *link2) { datalink_id_t linkid1 = DATALINK_INVALID_LINKID; datalink_id_t linkid2 = DATALINK_INVALID_LINKID; @@ -626,11 +638,11 @@ dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2) boolean_t remphy2 = B_FALSE; dladm_status_t status; - (void) dladm_name2info(handle, link1, &linkid1, &flags1, &class1, - &media1); - if ((dladm_name2info(handle, link2, &linkid2, &flags2, &class2, - &media2) == DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) && - (flags2 == DLADM_OPT_PERSIST)) { + (void) dladm_zname2info(handle, zonename, link1, &linkid1, &flags1, + &class1, &media1); + if ((dladm_zname2info(handle, zonename, link2, &linkid2, &flags2, + &class2, &media2) == DLADM_STATUS_OK) && + (class2 == DATALINK_CLASS_PHYS) && (flags2 == DLADM_OPT_PERSIST)) { /* * see whether link2 is a removed physical link. */ @@ -644,7 +656,7 @@ dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2) * does not exist. */ status = i_dladm_rename_link_c1(handle, linkid1, link1, - link2, flags1); + link2, flags1, zonename); } else if (remphy2) { /* * case 2: rename an available link to a REMOVED diff --git a/usr/src/lib/libdladm/common/libdllink.h b/usr/src/lib/libdladm/common/libdllink.h index a2830b5e37..a858e78aa3 100644 --- a/usr/src/lib/libdladm/common/libdllink.h +++ b/usr/src/lib/libdladm/common/libdllink.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #ifndef _LIBDLLINK_H @@ -121,7 +122,7 @@ extern dladm_status_t dladm_info(dladm_handle_t, datalink_id_t, dladm_attr_t *); extern dladm_status_t dladm_rename_link(dladm_handle_t, const char *, - const char *); + const char *, const char *); extern dladm_status_t dladm_set_linkprop(dladm_handle_t, datalink_id_t, const char *, char **, uint_t, uint_t); @@ -170,6 +171,9 @@ extern dladm_status_t dladm_up_datalink_id(dladm_handle_t, datalink_id_t); extern dladm_status_t dladm_name2info(dladm_handle_t, const char *, datalink_id_t *, uint32_t *, datalink_class_t *, uint32_t *); +extern dladm_status_t dladm_zname2info(dladm_handle_t, const char *, + const char *, datalink_id_t *, uint32_t *, + datalink_class_t *, uint32_t *); extern dladm_status_t dladm_datalink_id2info(dladm_handle_t, datalink_id_t, uint32_t *, datalink_class_t *, uint32_t *, char *, size_t); diff --git a/usr/src/lib/libdladm/common/libdlmgmt.c b/usr/src/lib/libdladm/common/libdlmgmt.c index 66b7bbce09..8f999d8b9c 100644 --- a/usr/src/lib/libdladm/common/libdlmgmt.c +++ b/usr/src/lib/libdladm/common/libdlmgmt.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2016 Joyent, Inc. */ #include <door.h> @@ -124,6 +125,7 @@ dladm_create_datalink_id(dladm_handle_t handle, const char *link, dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0; dlmgmt_flags |= (flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0; + dlmgmt_flags |= (flags & DLADM_OPT_TRANSIENT) ? DLMGMT_TRANSIENT : 0; (void) strlcpy(createid.ld_link, link, MAXLINKNAMELEN); createid.ld_class = class; @@ -285,6 +287,7 @@ dladm_walk_datalink_id(int (*fn)(dladm_handle_t, datalink_id_t, void *), dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0; dlmgmt_flags |= ((flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0); + dlmgmt_flags |= ((flags & DLADM_OPT_TRANSIENT) ? DLMGMT_TRANSIENT : 0); getnext.ld_cmd = DLMGMT_CMD_GETNEXT; getnext.ld_class = class; @@ -528,12 +531,24 @@ dladm_getnext_conf_linkprop(dladm_handle_t handle, dladm_conf_t conf, } /* - * Get the link ID that is associated with the given name. + * Get the link ID that is associated with the given name in the current zone. */ dladm_status_t dladm_name2info(dladm_handle_t handle, const char *link, datalink_id_t *linkidp, uint32_t *flagp, datalink_class_t *classp, uint32_t *mediap) { + return (dladm_zname2info(handle, NULL, link, linkidp, flagp, classp, + mediap)); +} + +/* + * Get the link ID that is associated with the given zone/name pair. + */ +dladm_status_t +dladm_zname2info(dladm_handle_t handle, const char *zonename, const char *link, + datalink_id_t *linkidp, uint32_t *flagp, datalink_class_t *classp, + uint32_t *mediap) +{ dlmgmt_door_getlinkid_t getlinkid; dlmgmt_getlinkid_retval_t retval; datalink_id_t linkid; @@ -542,6 +557,10 @@ dladm_name2info(dladm_handle_t handle, const char *link, datalink_id_t *linkidp, getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID; (void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN); + if (zonename != NULL) + getlinkid.ld_zoneid = getzoneidbyname(zonename); + else + getlinkid.ld_zoneid = -1; if ((status = dladm_door_call(handle, &getlinkid, sizeof (getlinkid), &retval, &sz)) != DLADM_STATUS_OK) { @@ -621,10 +640,12 @@ dladm_datalink_id2info(dladm_handle_t handle, datalink_id_t linkid, if (mediap != NULL) *mediap = retval.lr_media; if (flagp != NULL) { - *flagp = retval.lr_flags & DLMGMT_ACTIVE ? + *flagp = (retval.lr_flags & DLMGMT_ACTIVE) ? DLADM_OPT_ACTIVE : 0; *flagp |= (retval.lr_flags & DLMGMT_PERSIST) ? DLADM_OPT_PERSIST : 0; + *flagp |= (retval.lr_flags & DLMGMT_TRANSIENT) ? + DLADM_OPT_TRANSIENT : 0; } return (DLADM_STATUS_OK); } diff --git a/usr/src/lib/libdladm/common/libdloverlay.c b/usr/src/lib/libdladm/common/libdloverlay.c new file mode 100644 index 0000000000..a83105b91c --- /dev/null +++ b/usr/src/lib/libdladm/common/libdloverlay.c @@ -0,0 +1,908 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015 Joyent, Inc. + */ + +#include <libdladm_impl.h> +#include <libdllink.h> +#include <libdloverlay.h> +#include <sys/dld.h> +#include <sys/overlay.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <limits.h> +#include <libvarpd_client.h> + +#define VARPD_PROPERTY_NAME "varpd/id" + +static const char *dladm_overlay_doorpath = "/var/run/varpd/varpd.door"; + +typedef struct dladm_overlay_propinfo { + boolean_t dop_isvarpd; + union { + overlay_ioc_propinfo_t *dop_overlay; + varpd_client_prop_handle_t *dop_varpd; + } dop_un; +} dladm_overlay_propinfo_t; + +dladm_status_t +dladm_overlay_prop_info(dladm_overlay_propinfo_handle_t phdl, + const char **namep, uint_t *typep, uint_t *protp, const void **defp, + uint32_t *sizep, const mac_propval_range_t **possp) +{ + dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)phdl; + overlay_ioc_propinfo_t *oinfop = infop->dop_un.dop_overlay; + + if (infop->dop_isvarpd == B_FALSE) { + if (namep != NULL) + *namep = oinfop->oipi_name; + if (typep != NULL) + *typep = oinfop->oipi_type; + if (protp != NULL) + *protp = oinfop->oipi_prot; + if (defp != NULL) + *defp = oinfop->oipi_default; + if (sizep != NULL) + *sizep = oinfop->oipi_defsize; + if (possp != NULL) { + /* LINTED: E_BAD_PTR_CAST_ALIGN */ + *possp = (const mac_propval_range_t *)oinfop->oipi_poss; + } + + } else { + int ret; + ret = libvarpd_c_prop_info(infop->dop_un.dop_varpd, namep, + typep, protp, defp, sizep, possp); + if (ret != 0) + return (dladm_errno2status(ret)); + + } + + return (DLADM_STATUS_OK); +} + +static dladm_status_t +dladm_overlay_parse_prop(overlay_prop_type_t type, void *buf, uint32_t *sizep, + const char *val) +{ + int ret; + int64_t ival; + uint64_t uval; + char *eptr; + struct in6_addr ipv6; + struct in_addr ip; + + switch (type) { + case OVERLAY_PROP_T_INT: + errno = 0; + ival = strtol(val, &eptr, 10); + if ((ival == 0 && errno == EINVAL) || + ((ival == LONG_MAX || ival == LONG_MIN) && + errno == ERANGE)) + return (DLADM_STATUS_BADARG); + bcopy(&ival, buf, sizeof (int64_t)); + *sizep = sizeof (int64_t); + break; + case OVERLAY_PROP_T_UINT: + errno = 0; + uval = strtol(val, &eptr, 10); + if ((uval == 0 && errno == EINVAL) || + (uval == ULONG_MAX && errno == ERANGE)) + return (DLADM_STATUS_BADARG); + bcopy(&uval, buf, sizeof (uint64_t)); + *sizep = sizeof (uint64_t); + break; + case OVERLAY_PROP_T_STRING: + ret = strlcpy((char *)buf, val, OVERLAY_PROP_SIZEMAX); + if (ret >= OVERLAY_PROP_SIZEMAX) + return (DLADM_STATUS_BADARG); + *sizep = ret + 1; + break; + case OVERLAY_PROP_T_IP: + /* + * Always try to parse the IP as an IPv6 address. If that fails, + * try to interpret it as an IPv4 address and transform it into + * an IPv6 mapped IPv4 address. + */ + if (inet_pton(AF_INET6, val, &ipv6) != 1) { + if (inet_pton(AF_INET, val, &ip) != 1) + return (DLADM_STATUS_BADARG); + + IN6_INADDR_TO_V4MAPPED(&ip, &ipv6); + } + bcopy(&ipv6, buf, sizeof (struct in6_addr)); + *sizep = sizeof (struct in6_addr); + break; + default: + abort(); + } + + return (DLADM_STATUS_OK); +} + +/* ARGSUSED */ +static dladm_status_t +dladm_overlay_varpd_setprop(dladm_handle_t handle, varpd_client_handle_t *chdl, + uint64_t inst, const char *name, char *const *valp, uint_t cnt) +{ + int ret; + uint32_t size; + uint8_t buf[LIBVARPD_PROP_SIZEMAX]; + varpd_client_prop_handle_t *phdl; + uint_t type; + dladm_status_t status; + + if ((ret = libvarpd_c_prop_handle_alloc(chdl, inst, &phdl)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_prop_info_fill_by_name(phdl, name)) != 0) { + libvarpd_c_prop_handle_free(phdl); + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_prop_info(phdl, NULL, &type, NULL, NULL, NULL, + NULL)) != 0) { + libvarpd_c_prop_handle_free(phdl); + return (dladm_errno2status(ret)); + } + + if ((status = dladm_overlay_parse_prop(type, buf, &size, valp[0])) != + DLADM_STATUS_OK) { + libvarpd_c_prop_handle_free(phdl); + return (status); + } + + ret = libvarpd_c_prop_set(phdl, buf, size); + libvarpd_c_prop_handle_free(phdl); + + return (dladm_errno2status(ret)); +} + +dladm_status_t +dladm_overlay_setprop(dladm_handle_t handle, datalink_id_t linkid, + const char *name, char *const *valp, uint_t cnt) +{ + int ret; + dladm_status_t status; + overlay_ioc_propinfo_t info; + overlay_ioc_prop_t prop; + + if (linkid == DATALINK_INVALID_LINKID || + name == NULL || valp == NULL || cnt != 1) + return (DLADM_STATUS_BADARG); + + bzero(&info, sizeof (overlay_ioc_propinfo_t)); + info.oipi_linkid = linkid; + info.oipi_id = -1; + if (strlcpy(info.oipi_name, name, OVERLAY_PROP_NAMELEN) >= + OVERLAY_PROP_NAMELEN) + return (DLADM_STATUS_BADARG); + + status = DLADM_STATUS_OK; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &info); + if (ret != 0) + status = dladm_errno2status(errno); + + if (status != DLADM_STATUS_OK) + return (status); + + prop.oip_linkid = linkid; + prop.oip_id = info.oipi_id; + prop.oip_name[0] = '\0'; + if ((ret = dladm_overlay_parse_prop(info.oipi_type, prop.oip_value, + &prop.oip_size, valp[0])) != DLADM_STATUS_OK) + return (ret); + + status = DLADM_STATUS_OK; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_SETPROP, &prop); + if (ret != 0) + status = dladm_errno2status(errno); + + return (ret); +} + +/* + * Tell the user about any unset required properties. + */ +static int +dladm_overlay_activate_cb(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_propinfo_handle_t phdl, void *arg) +{ + dladm_status_t status; + uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX]; + uint_t prot; + size_t size = sizeof (buf); + const char *name; + dladm_errlist_t *errs = arg; + + if ((status = dladm_overlay_prop_info(phdl, &name, NULL, &prot, NULL, + NULL, NULL)) != DLADM_STATUS_OK) + return (status); + + if ((prot & OVERLAY_PROP_PERM_REQ) == 0) + return (DLADM_WALK_CONTINUE); + + if (dladm_overlay_get_prop(handle, linkid, phdl, buf, &size) != + DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + if (size == 0) + (void) dladm_errlist_append(errs, "unset required property: %s", + name); + + return (DLADM_WALK_CONTINUE); +} + +/* + * We need to clean up the world here. The problem is that we may or may not + * actually have everything created. While in the normal case, we'd always have + * an overlay device, assigned datalink id, and a varpd instance, we might not + * have any of those, except for the datalink instance. Therefore, as long as + * the id refers to a valid overlay, we should try to clean up as much of the + * state as possible and most importantly, we need to make sure we delete the + * datalink id. If we fail to do that, then that name will become lost to time. + */ +dladm_status_t +dladm_overlay_delete(dladm_handle_t handle, datalink_id_t linkid) +{ + datalink_class_t class; + overlay_ioc_delete_t oid; + varpd_client_handle_t *chdl; + int ret; + uint32_t flags; + uint64_t varpdid; + + if (dladm_datalink_id2info(handle, linkid, &flags, &class, NULL, + NULL, 0) != DLADM_STATUS_OK) + return (DLADM_STATUS_BADARG); + + if (class != DATALINK_CLASS_OVERLAY) + return (DLADM_STATUS_BADARG); + + oid.oid_linkid = linkid; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_DELETE, &oid); + if (ret != 0 && errno != ENOENT) { + return (dladm_errno2status(errno)); + } + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) { + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) { + if (ret == ENOENT) { + goto finish; + } + (void) libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + ret = libvarpd_c_instance_destroy(chdl, varpdid); +finish: + (void) libvarpd_c_destroy(chdl); + (void) dladm_destroy_datalink_id(handle, linkid, flags); + + return (dladm_errno2status(ret)); +} + +dladm_status_t +dladm_overlay_get_prop(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_propinfo_handle_t infohdl, void *buf, size_t *sizep) +{ + int ret; + overlay_ioc_prop_t oip; + dladm_overlay_propinfo_t *infop = (dladm_overlay_propinfo_t *)infohdl; + + /* + * It'd be nice if we had a better or more specific error for this. If + * this kind of error becomes common place, let's get a better dladm + * error. + */ + if (*sizep < DLADM_OVERLAY_PROP_SIZEMAX) + return (dladm_errno2status(ERANGE)); + + if (infop->dop_isvarpd == B_FALSE) { + bzero(&oip, sizeof (overlay_ioc_prop_t)); + oip.oip_linkid = linkid; + oip.oip_id = infop->dop_un.dop_overlay->oipi_id; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_GETPROP, &oip); + if (ret != 0) + return (dladm_errno2status(errno)); + bcopy(oip.oip_value, buf, DLADM_OVERLAY_PROP_SIZEMAX); + *sizep = oip.oip_size; + } else { + uint32_t size = *sizep; + + ret = libvarpd_c_prop_get(infop->dop_un.dop_varpd, buf, &size); + if (ret != 0) + return (dladm_errno2status(errno)); + *sizep = size; + } + + return (DLADM_STATUS_OK); +} + +static dladm_status_t +dladm_overlay_walk_varpd_prop(dladm_handle_t handle, datalink_id_t linkid, + uint64_t varpdid, dladm_overlay_prop_f func, void *arg) +{ + int ret, i; + varpd_client_handle_t *chdl; + varpd_client_prop_handle_t *phdl; + uint_t nprops; + dladm_status_t status; + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_prop_handle_alloc(chdl, varpdid, &phdl)) != 0) { + (void) libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_prop_nprops(chdl, varpdid, &nprops)) != 0) { + libvarpd_c_prop_handle_free(phdl); + (void) libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + status = DLADM_STATUS_OK; + for (i = 0; i < nprops; i++) { + dladm_overlay_propinfo_t dop; + + bzero(&dop, sizeof (dop)); + dop.dop_isvarpd = B_TRUE; + dop.dop_un.dop_varpd = phdl; + + if ((ret = libvarpd_c_prop_info_fill(phdl, i)) != 0) { + status = dladm_errno2status(ret); + break; + } + + ret = func(handle, linkid, + (dladm_overlay_propinfo_handle_t)&dop, arg); + if (ret == DLADM_WALK_TERMINATE) + break; + } + + libvarpd_c_prop_handle_free(phdl); + libvarpd_c_destroy(chdl); + + return (status); +} + +dladm_status_t +dladm_overlay_walk_prop(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_prop_f func, void *arg, dladm_errlist_t *errs) +{ + int i, ret; + char buf[MAXLINKNAMELEN]; + char errmsg[DLADM_STRSIZE]; + datalink_class_t class; + dladm_status_t info_status; + overlay_ioc_nprops_t oin; + overlay_ioc_propinfo_t oipi; + dladm_overlay_propinfo_t dop; + uint64_t varpdid = UINT64_MAX; + + if ((info_status = dladm_datalink_id2info(handle, linkid, NULL, &class, + NULL, buf, MAXLINKNAMELEN)) != DLADM_STATUS_OK) { + (void) dladm_errlist_append(errs, "failed to get info for " + "datalink id %u: %s", + linkid, dladm_status2str(info_status, errmsg)); + return (DLADM_STATUS_BADARG); + } + + if (class != DATALINK_CLASS_OVERLAY) { + (void) dladm_errlist_append(errs, "%s is not an overlay", buf); + return (DLADM_STATUS_BADARG); + } + + bzero(&oin, sizeof (overlay_ioc_nprops_t)); + oin.oipn_linkid = linkid; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_NPROPS, &oin); + if (ret != 0) { + (void) dladm_errlist_append(errs, "failed to get " + "overlay properties for overlay %s: %s", + buf, strerror(errno)); + return (dladm_errno2status(errno)); + } + + for (i = 0; i < oin.oipn_nprops; i++) { + bzero(&dop, sizeof (dladm_overlay_propinfo_t)); + bzero(&oipi, sizeof (overlay_ioc_propinfo_t)); + oipi.oipi_linkid = linkid; + oipi.oipi_id = i; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_PROPINFO, &oipi); + if (ret != 0) { + (void) dladm_errlist_append(errs, "failed to get " + "propinfo for overlay %s, property %d: %s", + buf, i, strerror(errno)); + return (dladm_errno2status(errno)); + } + + dop.dop_isvarpd = B_FALSE; + dop.dop_un.dop_overlay = &oipi; + ret = func(handle, linkid, + (dladm_overlay_propinfo_handle_t)&dop, arg); + if (ret == DLADM_WALK_TERMINATE) + break; + + if (strcmp(oipi.oipi_name, VARPD_PROPERTY_NAME) == 0) { + uint8_t buf[DLADM_OVERLAY_PROP_SIZEMAX]; + size_t bufsize = sizeof (buf); + uint64_t *vp; + + if (dladm_overlay_get_prop(handle, linkid, + (dladm_overlay_propinfo_handle_t)&dop, buf, + &bufsize) != DLADM_STATUS_OK) + continue; + + /* LINTED: E_BAD_PTR_CAST_ALIGN */ + vp = (uint64_t *)buf; + varpdid = *vp; + } + } + + /* Should this really be possible? */ + if (varpdid == UINT64_MAX) + return (DLADM_STATUS_OK); + + ret = dladm_overlay_walk_varpd_prop(handle, linkid, varpdid, func, + arg); + if (ret != DLADM_STATUS_OK) { + (void) dladm_errlist_append(errs, + "failed to get varpd props for " + "overlay %s, varpd id %llu: %s", + buf, varpdid, dladm_status2str(info_status, errmsg)); + } + return (ret); +} + +dladm_status_t +dladm_overlay_create(dladm_handle_t handle, const char *name, + const char *encap, const char *search, uint64_t vid, + dladm_arg_list_t *props, dladm_errlist_t *errs, uint32_t flags) +{ + int ret, i; + dladm_status_t status; + datalink_id_t linkid; + overlay_ioc_create_t oic; + overlay_ioc_activate_t oia; + size_t slen; + varpd_client_handle_t *vch; + uint64_t id; + + status = dladm_create_datalink_id(handle, name, DATALINK_CLASS_OVERLAY, + DL_ETHER, flags, &linkid); + if (status != DLADM_STATUS_OK) + return (status); + + bzero(&oic, sizeof (oic)); + oic.oic_linkid = linkid; + oic.oic_vnetid = vid; + (void) strlcpy(oic.oic_encap, encap, MAXLINKNAMELEN); + + status = DLADM_STATUS_OK; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_CREATE, &oic); + if (ret != 0) { + /* + * It'd be nice if we had private errors so we could better + * distinguish between different classes of errors. + */ + status = dladm_errno2status(errno); + } + + if (status != DLADM_STATUS_OK) { + (void) dladm_destroy_datalink_id(handle, linkid, flags); + return (status); + } + + slen = strlen(search); + for (i = 0; props != NULL && i < props->al_count; i++) { + dladm_arg_info_t *aip = &props->al_info[i]; + + /* + * If it's a property for the search plugin, eg. it has the + * prefix '<search>/', then we don't set the property on the + * overlay device and instead set it on the varpd instance. + */ + if (strncmp(aip->ai_name, search, slen) == 0 && + aip->ai_name[slen] == '/') + continue; + status = dladm_overlay_setprop(handle, linkid, aip->ai_name, + aip->ai_val, aip->ai_count); + if (status != DLADM_STATUS_OK) { + (void) dladm_errlist_append(errs, + "failed to set property %s", + aip->ai_name); + (void) dladm_overlay_delete(handle, linkid); + return (status); + } + } + + if ((ret = libvarpd_c_create(&vch, dladm_overlay_doorpath)) != 0) { + (void) dladm_errlist_append(errs, + "failed to create libvarpd handle: %s", strerror(ret)); + (void) dladm_overlay_delete(handle, linkid); + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_instance_create(vch, linkid, search, + &id)) != 0) { + (void) dladm_errlist_append(errs, + "failed to create varpd instance: %s", strerror(ret)); + libvarpd_c_destroy(vch); + (void) dladm_overlay_delete(handle, linkid); + return (dladm_errno2status(ret)); + } + + for (i = 0; props != NULL && i < props->al_count; i++) { + dladm_arg_info_t *aip = &props->al_info[i]; + + /* + * Skip arguments we've processed already. + */ + if (strncmp(aip->ai_name, search, slen) != 0) + continue; + + if (aip->ai_name[slen] != '/') + continue; + + ret = dladm_overlay_varpd_setprop(handle, vch, id, aip->ai_name, + aip->ai_val, aip->ai_count); + if (ret != 0) { + (void) dladm_errlist_append(errs, + "failed to set varpd prop: %s\n", + aip->ai_name); + (void) libvarpd_c_instance_destroy(vch, id); + libvarpd_c_destroy(vch); + (void) dladm_overlay_delete(handle, linkid); + return (dladm_errno2status(ret)); + } + } + + if ((ret = libvarpd_c_instance_activate(vch, id)) != 0) { + (void) dladm_errlist_append(errs, + "failed to activate varpd instance: %s", strerror(ret)); + (void) dladm_overlay_walk_varpd_prop(handle, linkid, id, + dladm_overlay_activate_cb, errs); + (void) libvarpd_c_instance_destroy(vch, id); + libvarpd_c_destroy(vch); + (void) dladm_overlay_delete(handle, linkid); + return (dladm_errno2status(ret)); + + } + + bzero(&oia, sizeof (oia)); + oia.oia_linkid = linkid; + status = DLADM_STATUS_OK; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_ACTIVATE, &oia); + if (ret != 0) { + ret = errno; + (void) dladm_errlist_append(errs, "failed to activate " + "device: %s", strerror(ret)); + (void) libvarpd_c_instance_destroy(vch, id); + (void) dladm_overlay_walk_prop(handle, linkid, + dladm_overlay_activate_cb, errs, errs); + status = dladm_errno2status(ret); + (void) libvarpd_c_instance_destroy(vch, id); + } + + libvarpd_c_destroy(vch); + if (status != DLADM_STATUS_OK) + (void) dladm_overlay_delete(handle, linkid); + + return (status); +} + + + +typedef struct overlay_walk_cb { + dladm_handle_t owc_handle; + datalink_id_t owc_linkid; + void *owc_arg; + dladm_overlay_cache_f owc_func; + uint_t owc_mode; + uint_t owc_dest; +} overlay_walk_cb_t; + +/* ARGSUSED */ +static int +dladm_overlay_walk_cache_cb(varpd_client_handle_t *chdl, uint64_t varpdid, + const struct ether_addr *key, const varpd_client_cache_entry_t *entry, + void *arg) +{ + overlay_walk_cb_t *owc = arg; + dladm_overlay_point_t point; + + bzero(&point, sizeof (dladm_overlay_point_t)); + point.dop_dest = owc->owc_dest; + point.dop_mac = entry->vcp_mac; + point.dop_flags = entry->vcp_flags; + point.dop_ip = entry->vcp_ip; + point.dop_port = entry->vcp_port; + + if (owc->owc_mode == OVERLAY_TARGET_POINT) + point.dop_flags |= DLADM_OVERLAY_F_DEFAULT; + + if (owc->owc_func(owc->owc_handle, owc->owc_linkid, key, &point, + owc->owc_arg) == DLADM_WALK_TERMINATE) + return (1); + return (0); +} + +dladm_status_t +dladm_overlay_walk_cache(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_cache_f func, void *arg) +{ + int ret; + uint_t mode, dest; + uint64_t varpdid; + varpd_client_handle_t *chdl; + overlay_walk_cb_t cbarg; + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid, + &dest, &mode)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + cbarg.owc_handle = handle; + cbarg.owc_linkid = linkid; + cbarg.owc_arg = arg; + cbarg.owc_func = func; + cbarg.owc_dest = dest; + cbarg.owc_mode = mode; + ret = libvarpd_c_instance_cache_walk(chdl, varpdid, + dladm_overlay_walk_cache_cb, &cbarg); + libvarpd_c_destroy(chdl); + + return (dladm_errno2status(ret)); +} + +/* ARGSUSED */ +dladm_status_t +dladm_overlay_cache_flush(dladm_handle_t handle, datalink_id_t linkid) +{ + int ret; + uint64_t varpdid; + varpd_client_handle_t *chdl; + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + ret = libvarpd_c_instance_cache_flush(chdl, varpdid); + libvarpd_c_destroy(chdl); + + return (dladm_errno2status(ret)); +} + +/* ARGSUSED */ +dladm_status_t +dladm_overlay_cache_delete(dladm_handle_t handle, datalink_id_t linkid, + const struct ether_addr *key) +{ + int ret; + uint64_t varpdid; + varpd_client_handle_t *chdl; + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + ret = libvarpd_c_instance_cache_delete(chdl, varpdid, key); + libvarpd_c_destroy(chdl); + + return (dladm_errno2status(ret)); +} + +/* ARGSUSED */ +dladm_status_t +dladm_overlay_cache_set(dladm_handle_t handle, datalink_id_t linkid, + const struct ether_addr *key, char *val) +{ + int ret; + uint_t dest; + uint64_t varpdid; + char *ip, *port = NULL; + varpd_client_handle_t *chdl; + varpd_client_cache_entry_t vcp; + + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid, + &dest, NULL)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + /* + * Mode tells us what we should expect in val. It we have more than one + * thing listed, the canonical format of it right now is mac,ip:port. + */ + bzero(&vcp, sizeof (varpd_client_cache_entry_t)); + + if (strcasecmp(val, "drop") == 0) { + vcp.vcp_flags = OVERLAY_TARGET_CACHE_DROP; + goto send; + } + + if (dest & OVERLAY_PLUGIN_D_ETHERNET) { + if (ether_aton_r(val, &vcp.vcp_mac) == NULL) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(EINVAL)); + } + } + + if (dest & OVERLAY_PLUGIN_D_IP) { + if (dest & OVERLAY_PLUGIN_D_ETHERNET) { + if ((ip = strchr(val, ',')) == NULL) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + ip++; + } else { + ip = val; + } + + if (dest & OVERLAY_PLUGIN_D_PORT) { + if ((port = strchr(val, ':')) == NULL) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + *port = '\0'; + port++; + } + + /* Try v6, then fall back to v4 */ + ret = inet_pton(AF_INET6, ip, &vcp.vcp_ip); + if (ret == -1) + abort(); + if (ret == 0) { + struct in_addr v4; + + ret = inet_pton(AF_INET, ip, &v4); + if (ret == -1) + abort(); + if (ret == 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + IN6_INADDR_TO_V4MAPPED(&v4, &vcp.vcp_ip); + } + } + + if (dest & OVERLAY_PLUGIN_D_PORT) { + char *eptr; + unsigned long l; + if (port == NULL && (dest & OVERLAY_PLUGIN_D_ETHERNET)) { + if ((port = strchr(val, ',')) == NULL) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(EINVAL)); + } + } else if (port == NULL) + port = val; + + errno = 0; + l = strtoul(port, &eptr, 10); + if (errno != 0 || *eptr != '\0') { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(EINVAL)); + } + if (l == 0 || l > UINT16_MAX) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(EINVAL)); + } + vcp.vcp_port = l; + } + +send: + ret = libvarpd_c_instance_cache_set(chdl, varpdid, key, &vcp); + + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); +} + +/* ARGSUSED */ +dladm_status_t +dladm_overlay_cache_get(dladm_handle_t handle, datalink_id_t linkid, + const struct ether_addr *key, dladm_overlay_point_t *point) +{ + int ret; + uint_t dest, mode; + uint64_t varpdid; + varpd_client_handle_t *chdl; + varpd_client_cache_entry_t entry; + + if ((ret = libvarpd_c_create(&chdl, dladm_overlay_doorpath)) != 0) + return (dladm_errno2status(ret)); + + if ((ret = libvarpd_c_instance_lookup(chdl, linkid, &varpdid)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + if ((ret = libvarpd_c_instance_target_mode(chdl, varpdid, + &dest, &mode)) != 0) { + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); + } + + ret = libvarpd_c_instance_cache_get(chdl, varpdid, key, &entry); + if (ret == 0) { + point->dop_dest = dest; + point->dop_mac = entry.vcp_mac; + point->dop_flags = entry.vcp_flags; + point->dop_ip = entry.vcp_ip; + point->dop_port = entry.vcp_port; + if (mode == OVERLAY_TARGET_POINT) + point->dop_flags |= DLADM_OVERLAY_F_DEFAULT; + } + + libvarpd_c_destroy(chdl); + return (dladm_errno2status(ret)); +} + +dladm_status_t +dladm_overlay_status(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_status_f func, void *arg) +{ + int ret; + dladm_status_t status; + overlay_ioc_status_t ois; + dladm_overlay_status_t dos; + + ois.ois_linkid = linkid; + status = DLADM_STATUS_OK; + ret = ioctl(dladm_dld_fd(handle), OVERLAY_IOC_STATUS, &ois); + if (ret != 0) + status = dladm_errno2status(errno); + if (status != DLADM_STATUS_OK) + return (status); + + dos.dos_degraded = ois.ois_status == OVERLAY_I_DEGRADED ? B_TRUE : + B_FALSE; + (void) strlcpy(dos.dos_fmamsg, ois.ois_message, + sizeof (dos.dos_fmamsg)); + func(handle, linkid, &dos, arg); + return (DLADM_STATUS_OK); +} diff --git a/usr/src/lib/libdladm/common/libdloverlay.h b/usr/src/lib/libdladm/common/libdloverlay.h new file mode 100644 index 0000000000..39b01ccae3 --- /dev/null +++ b/usr/src/lib/libdladm/common/libdloverlay.h @@ -0,0 +1,107 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015 Joyent, Inc. + */ + +#ifndef _LIBDLOVERLAY_H +#define _LIBDLOVERLAY_H + +/* + * libdladm Overlay device routines + */ + +#include <libdladm.h> +#include <libdladm_impl.h> +#include <sys/overlay.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define DLADM_OVERLAY_F_DROP 0x0001 +#define DLADM_OVERLAY_F_DEFAULT 0xf000 + +typedef struct dladm_overlay_point { + uint_t dop_dest; + struct ether_addr dop_mac; + uint16_t dop_flags; + struct in6_addr dop_ip; + uint16_t dop_port; +} dladm_overlay_point_t; + +typedef struct dladm_overlay_status { + boolean_t dos_degraded; + char dos_fmamsg[256]; +} dladm_overlay_status_t; + +extern dladm_status_t dladm_overlay_create(dladm_handle_t, const char *, + const char *, const char *, uint64_t, dladm_arg_list_t *, dladm_errlist_t *, + uint32_t); +extern dladm_status_t dladm_overlay_delete(dladm_handle_t, datalink_id_t); + +typedef void (*dladm_overlay_status_f)(dladm_handle_t, datalink_id_t, + dladm_overlay_status_t *, void *); +extern dladm_status_t dladm_overlay_status(dladm_handle_t, datalink_id_t, + dladm_overlay_status_f, void *); + +extern dladm_status_t dladm_overlay_cache_flush(dladm_handle_t, datalink_id_t); +extern dladm_status_t dladm_overlay_cache_delete(dladm_handle_t, datalink_id_t, + const struct ether_addr *); +extern dladm_status_t dladm_overlay_cache_set(dladm_handle_t, datalink_id_t, + const struct ether_addr *, char *); +extern dladm_status_t dladm_overlay_cache_get(dladm_handle_t, datalink_id_t, + const struct ether_addr *, dladm_overlay_point_t *); + +#define DLADM_OVERLAY_PROP_SIZEMAX 256 +#define DLADM_OVERLAY_PROP_NAMELEN 32 + +typedef struct __dladm_overlay_propinfo *dladm_overlay_propinfo_handle_t; + +extern dladm_status_t dladm_overlay_prop_info(dladm_overlay_propinfo_handle_t, + const char **, uint_t *, uint_t *, const void **, uint32_t *, + const mac_propval_range_t **); +extern dladm_status_t dladm_overlay_get_prop(dladm_handle_t, datalink_id_t, + dladm_overlay_propinfo_handle_t, void *buf, size_t *bufsize); + +typedef int (*dladm_overlay_prop_f)(dladm_handle_t, datalink_id_t, + dladm_overlay_propinfo_handle_t, void *); +extern dladm_status_t dladm_overlay_walk_prop(dladm_handle_t, datalink_id_t, + dladm_overlay_prop_f, void *arg, dladm_errlist_t *); + +typedef int (*dladm_overlay_cache_f)(dladm_handle_t, datalink_id_t, + const struct ether_addr *, const dladm_overlay_point_t *, void *); +extern dladm_status_t dladm_overlay_walk_cache(dladm_handle_t, datalink_id_t, + dladm_overlay_cache_f, void *); + +/* + * Some day we'll want to support being able to set properties after creation. + * If we do, the following strawman API might serve us well. + * + * extern dladm_status_t dladm_overlay_prop_lookup(dladm_handle_t, + * datalink_id_t, const char *, dladm_overlay_propinfo_handle_t *); + * extern void dladm_overlay_prop_handle_free(dladm_handle_t, datalink_id_t, + * dladm_overlay_propinfo_handle_t *); + * extern dladm_status_t dladm_overlay_set_prop(dladm_handle_t, datalink_id_t, + * dladm_propinfo_handle_t, void *buf, size_t *bufsize); + * extern dladm_status_t dladm_overlay_str_to_buf(dladm_handle_t, datalink_id_t, + * dladm_overlay_propinfo_handle_t *, const char *, void *, size_t *); + * extern dladm_status_t dladm_overlay_buf_to_str(dladm_handle_t, datalink_id_t, + * dladm_overlay_propinfo_handle_t *, const void *, const size_t, char *, + * size_t *); + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDLOVERLAY_H */ diff --git a/usr/src/lib/libdladm/common/libdlvlan.c b/usr/src/lib/libdladm/common/libdlvlan.c index 943728dc03..34c1e6682d 100644 --- a/usr/src/lib/libdladm/common/libdlvlan.c +++ b/usr/src/lib/libdladm/common/libdlvlan.c @@ -64,7 +64,7 @@ dladm_vlan_create(dladm_handle_t handle, const char *vlan, datalink_id_t linkid, { return (dladm_vnic_create(handle, vlan, linkid, VNIC_MAC_ADDR_TYPE_PRIMARY, NULL, 0, NULL, 0, vid, VRRP_VRID_NONE, - AF_UNSPEC, vlan_id_out, proplist, flags | DLADM_OPT_VLAN)); + AF_UNSPEC, vlan_id_out, proplist, NULL, flags | DLADM_OPT_VLAN)); } /* diff --git a/usr/src/lib/libdladm/common/libdlvnic.c b/usr/src/lib/libdladm/common/libdlvnic.c index bad25e69ed..7ff0cd5530 100644 --- a/usr/src/lib/libdladm/common/libdlvnic.c +++ b/usr/src/lib/libdladm/common/libdlvnic.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015, Joyent Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ @@ -400,7 +401,7 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid, vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, uint_t mac_len, int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid, int af, datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist, - uint32_t flags) + dladm_errlist_t *errs, uint32_t flags) { dladm_vnic_attr_t attr; datalink_id_t vnic_id; @@ -552,27 +553,40 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid, vnic_created = B_TRUE; /* Save vnic configuration and its properties */ - if (!(flags & DLADM_OPT_PERSIST)) - goto done; + if (flags & DLADM_OPT_PERSIST) { + status = dladm_vnic_persist_conf(handle, name, &attr, class); + if (status == DLADM_STATUS_OK) + conf_set = B_TRUE; + } - status = dladm_vnic_persist_conf(handle, name, &attr, class); - if (status != DLADM_STATUS_OK) - goto done; - conf_set = B_TRUE; +done: + if (status == DLADM_STATUS_OK && proplist != NULL) { + uint32_t flg; + + flg = flags & (DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE); - if (proplist != NULL) { for (i = 0; i < proplist->al_count; i++) { dladm_arg_info_t *aip = &proplist->al_info[i]; + if (strcmp(aip->ai_name, "zone") == 0 && + flags & DLADM_OPT_TRANSIENT) + flg |= DLADM_OPT_TRANSIENT; + else + flg &= ~DLADM_OPT_TRANSIENT; + status = dladm_set_linkprop(handle, vnic_id, - aip->ai_name, aip->ai_val, aip->ai_count, - DLADM_OPT_PERSIST); - if (status != DLADM_STATUS_OK) + aip->ai_name, aip->ai_val, aip->ai_count, flg); + if (status != DLADM_STATUS_OK) { + char errmsg[DLADM_STRSIZE]; + (void) dladm_errlist_append(errs, + "failed to set property %s: %s", + aip->ai_name, + dladm_status2str(status, errmsg)); break; + } } } -done: if (status != DLADM_STATUS_OK) { if (conf_set) (void) dladm_remove_conf(handle, vnic_id); diff --git a/usr/src/lib/libdladm/common/libdlvnic.h b/usr/src/lib/libdladm/common/libdlvnic.h index 94b656aadf..839b2de9f2 100644 --- a/usr/src/lib/libdladm/common/libdlvnic.h +++ b/usr/src/lib/libdladm/common/libdlvnic.h @@ -55,7 +55,8 @@ typedef struct dladm_vnic_attr { extern dladm_status_t dladm_vnic_create(dladm_handle_t, const char *, datalink_id_t, vnic_mac_addr_type_t, uchar_t *, uint_t, int *, uint_t, uint16_t, vrid_t, int, - datalink_id_t *, dladm_arg_list_t *, uint32_t); + datalink_id_t *, dladm_arg_list_t *, + dladm_errlist_t *, uint32_t); extern dladm_status_t dladm_vnic_delete(dladm_handle_t, datalink_id_t, uint32_t); diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c index 90edf1293f..3791784ab1 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -154,9 +154,9 @@ static pd_getf_t get_zone, get_autopush, get_rate_mod, get_rate, get_tagmode, get_range, get_stp, get_bridge_forward, get_bridge_pvid, get_protection, get_rxrings, get_txrings, get_cntavail, get_secondary_macs, - get_allowedips, get_allowedcids, get_pool, - get_rings_range, get_linkmode_prop, get_bits, - get_promisc_filtered; + get_allowallcids, get_allowedips, get_allowedcids, + get_pool, get_rings_range, get_linkmode_prop, get_bits, + get_promisc_filtered, get_dynamic_methods; static pd_setf_t set_zone, set_rate, set_powermode, set_radio, set_public_prop, set_resource, set_stp_prop, @@ -462,6 +462,18 @@ static val_desc_t link_protect_vals[] = { { "dhcp-nospoof", MPT_DHCPNOSPOOF }, }; +static val_desc_t link_dynamic_method_vals[] = { + { "dhcpv4", MPT_DYN_DHCPV4 }, + { "dhcpv6", MPT_DYN_DHCPV6 }, + { "slaac", MPT_DYN_SLAAC }, + { "addrconf", (MPT_DYN_SLAAC | MPT_DYN_DHCPV6) }, +}; + +static val_desc_t dladm_bool_vals[] = { + { "false", MPT_FALSE }, + { "true", MPT_TRUE }, +}; + static val_desc_t link_promisc_filtered_vals[] = { { "off", B_FALSE }, { "on", B_TRUE }, @@ -813,6 +825,11 @@ static prop_desc_t prop_table[] = { set_resource, NULL, get_protection, check_prop, 0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, + { "dynamic-methods", { "--", RESET_VAL }, + link_dynamic_method_vals, VALCNT(link_dynamic_method_vals), + set_resource, NULL, get_dynamic_methods, check_prop, 0, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, + { "promisc-filtered", { "on", 1 }, link_promisc_filtered_vals, VALCNT(link_promisc_filtered_vals), set_promisc_filtered, NULL, get_promisc_filtered, check_prop, 0, @@ -829,6 +846,11 @@ static prop_desc_t prop_table[] = { get_allowedcids, check_allowedcids, PD_CHECK_ALLOC, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, + { "allow-all-dhcp-cids", { "false", RESET_VAL }, + dladm_bool_vals, VALCNT(dladm_bool_vals), set_resource, NULL, + get_allowallcids, check_prop, 0, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, + { "rxrings", { "--", RESET_VAL }, NULL, 0, set_resource, get_rings_range, get_rxrings, check_rings, 0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE }, @@ -875,8 +897,10 @@ static resource_prop_t rsrc_prop_table[] = { {"pool", extract_pool}, {"pool-effective", extract_pool}, {"protection", extract_protection}, + {"dynamic-methods", extract_dynamic_methods}, {"allowed-ips", extract_allowedips}, {"allowed-dhcp-cids", extract_allowedcids}, + {"allow-all-dhcp-cids", extract_allowallcids}, {"rxrings", extract_rxrings}, {"rxrings-effective", extract_rxrings}, {"txrings", extract_txrings}, @@ -1672,6 +1696,9 @@ set_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid, if (zid_new == zid_old) return (DLADM_STATUS_OK); + if (flags & DLADM_OPT_TRANSIENT) + dzp->diz_transient = B_TRUE; + if ((status = set_public_prop(handle, pdp, linkid, vdp, val_cnt, flags, media)) != DLADM_STATUS_OK) return (status); @@ -2936,6 +2963,91 @@ dladm_str2cid(char *buf, mac_dhcpcid_t *cid) /* ARGSUSED */ static dladm_status_t +get_dynamic_methods(dladm_handle_t handle, prop_desc_t *pdp, + datalink_id_t linkid, char **prop_val, uint_t *val_cnt, + datalink_media_t media, uint_t flags, uint_t *perm_flags) +{ + mac_resource_props_t mrp; + mac_protect_t *p; + dladm_status_t status; + uint32_t i, cnt = 0, setbits[32]; + + status = i_dladm_get_public_prop(handle, linkid, "resource", flags, + perm_flags, &mrp, sizeof (mrp)); + if (status != DLADM_STATUS_OK) + return (status); + + p = &mrp.mrp_protect; + dladm_find_setbits32(p->mp_dynamic, setbits, &cnt); + if (cnt > *val_cnt) + return (DLADM_STATUS_BADVALCNT); + + for (i = 0; i < cnt; i++) + (void) dladm_dynamic2str( + setbits[i], prop_val[i], DLADM_STRSIZE); + + *val_cnt = cnt; + return (DLADM_STATUS_OK); +} + +dladm_status_t +extract_dynamic_methods(val_desc_t *vdp, uint_t cnt, void *arg) +{ + mac_resource_props_t *mrp = arg; + uint32_t methods = 0; + int i; + + for (i = 0; i < cnt; i++) + methods |= (uint32_t)vdp[i].vd_val; + + mrp->mrp_protect.mp_dynamic = methods; + mrp->mrp_mask |= MRP_PROTECT; + return (DLADM_STATUS_OK); +} + +/* ARGSUSED */ +static dladm_status_t +get_allowallcids(dladm_handle_t handle, prop_desc_t *pdp, + datalink_id_t linkid, char **prop_val, uint_t *val_cnt, + datalink_media_t media, uint_t flags, uint_t *perm_flags) +{ + mac_resource_props_t mrp; + mac_protect_t *p; + dladm_status_t status; + + if (*val_cnt < 1) + return (DLADM_STATUS_BADVALCNT); + + status = i_dladm_get_public_prop(handle, linkid, "resource", flags, + perm_flags, &mrp, sizeof (mrp)); + if (status != DLADM_STATUS_OK) + return (status); + + p = &mrp.mrp_protect; + (void) snprintf(*prop_val, DLADM_STRSIZE, + p->mp_allcids ? "true" : "false"); + *val_cnt = 1; + return (DLADM_STATUS_OK); +} + +/* ARGSUSED */ +dladm_status_t +extract_allowallcids(val_desc_t *vdp, uint_t cnt, void *arg) +{ + mac_resource_props_t *mrp = arg; + mac_protect_t *p = &mrp->mrp_protect; + + if (vdp->vd_val == RESET_VAL || vdp->vd_val == MPT_FALSE) { + p->mp_allcids = (boolean_t)RESET_VAL; + } else { + p->mp_allcids = (boolean_t)vdp->vd_val; + } + mrp->mrp_mask |= MRP_PROTECT; + return (DLADM_STATUS_OK); +} + +/* ARGSUSED */ +static dladm_status_t get_allowedcids(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid, char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags, uint_t *perm_flags) diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers index 63a86529fc..589bbf5330 100644 --- a/usr/src/lib/libdladm/common/mapfile-vers +++ b/usr/src/lib/libdladm/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015 Joyent, Inc. # # @@ -134,6 +135,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { dladm_remap_datalink_id; dladm_up_datalink_id; dladm_name2info; + dladm_zname2info; dladm_datalink_id2info; dladm_walk_datalink_id; dladm_create_conf; @@ -269,6 +271,23 @@ SYMBOL_VERSION SUNWprivate_1.1 { dladm_strs2range; dladm_range2list; dladm_list2range; + + dladm_errlist_init; + dladm_errlist_reset; + dladm_errlist_append; + + dladm_overlay_create; + dladm_overlay_delete; + dladm_overlay_status; + dladm_overlay_prop_info; + dladm_overlay_get_prop; + dladm_overlay_walk_prop; + + dladm_overlay_cache_set; + dladm_overlay_cache_get; + dladm_overlay_cache_delete; + dladm_overlay_cache_flush; + dladm_overlay_walk_cache; local: *; }; diff --git a/usr/src/lib/libdlpi/common/libdlpi.c b/usr/src/lib/libdlpi/common/libdlpi.c index 1a639a5db9..d07eea70e7 100644 --- a/usr/src/lib/libdlpi/common/libdlpi.c +++ b/usr/src/lib/libdlpi/common/libdlpi.c @@ -22,6 +22,9 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2017, Joyent, Inc. + */ /* * Data-Link Provider Interface (Version 2) @@ -51,7 +54,7 @@ #include "libdlpi_impl.h" -static int i_dlpi_open(const char *, int *, uint_t, boolean_t); +static int i_dlpi_open(const char *, const char *, int *, uint_t, boolean_t); static int i_dlpi_style1_open(dlpi_impl_t *); static int i_dlpi_style2_open(dlpi_impl_t *); static int i_dlpi_checkstyle(dlpi_impl_t *, t_uscalar_t); @@ -130,7 +133,8 @@ dlpi_walk(dlpi_walkfunc_t *fn, void *arg, uint_t flags) } int -dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags) +dlpi_open_zone(const char *linkname, const char *zonename, dlpi_handle_t *dhp, + uint_t flags) { int retval, on = 1; ifspec_t ifsp; @@ -164,6 +168,16 @@ dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags) if (getenv("DLPI_DEVONLY") != NULL) dip->dli_oflags |= DLPI_DEVONLY; + if (zonename == NULL) { + dip->dli_zonename[0] = '\0'; + } else { + if (strlcpy(dip->dli_zonename, zonename, + sizeof (dip->dli_zonename)) >= sizeof (dip->dli_zonename)) { + free(dip); + return (DLPI_EZONENAMEINVAL); + } + } + /* Copy linkname provided to the function. */ if (strlcpy(dip->dli_linkname, linkname, sizeof (dip->dli_linkname)) >= sizeof (dip->dli_linkname)) { @@ -237,6 +251,12 @@ dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags) return (DLPI_SUCCESS); } +int +dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags) +{ + return (dlpi_open_zone(linkname, NULL, dhp, flags)); +} + void dlpi_close(dlpi_handle_t dh) { @@ -1013,6 +1033,15 @@ dlpi_iftype(uint_t dlpitype) * /dev - if DLPI_DEVONLY is specified, or if there is no * data-link with the specified name (could be /dev/ip) * + * If a zone's name has been specified, eg. via dlpi_open_zone, then we instead + * will check in: + * + * /dev/ipnet/zone/%z/ - if DLPI_DEVIPNET is specified + * /dev/net/zone/%z/ - if a data-link with the specified name exists. + * + * When a zone name is specified, all of the fallback procedures that we opt for + * in the normal case are not used. + * * In particular, if DLPI_DEVIPNET is not specified, this function is used to * open a data-link node, or "/dev/ip" node. It is usually be called firstly * with style1 being B_TRUE, and if that fails and the return value is not @@ -1040,7 +1069,8 @@ dlpi_iftype(uint_t dlpitype) * the second style-2 open attempt. */ static int -i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1) +i_dlpi_open(const char *provider, const char *zonename, int *fd, uint_t flags, + boolean_t style1) { char path[MAXPATHLEN]; int oflags; @@ -1051,11 +1081,19 @@ i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1) oflags |= O_EXCL; if (flags & DLPI_DEVIPNET) { - (void) snprintf(path, sizeof (path), "/dev/ipnet/%s", provider); - if ((*fd = open(path, oflags)) != -1) + if (*zonename != '\0') { + (void) snprintf(path, sizeof (path), + "/dev/ipnet/zone/%s/%s", zonename, provider); + } else { + (void) snprintf(path, sizeof (path), "/dev/ipnet/%s", + provider); + } + if ((*fd = open(path, oflags)) != -1) { return (DLPI_SUCCESS); - else - return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR); + } else { + return (errno == ENOENT || errno == EISDIR ? + DLPI_ENOLINK : DL_SYSERR); + } } else if (style1 && !(flags & DLPI_DEVONLY)) { char driver[DLPI_LINKNAME_MAX]; char device[DLPI_LINKNAME_MAX]; @@ -1070,7 +1108,13 @@ i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1) if (dlpi_parselink(provider, driver, &ppa) != DLPI_SUCCESS) goto fallback; - (void) snprintf(path, sizeof (path), "/dev/net/%s", provider); + if (*zonename != '\0') { + (void) snprintf(path, sizeof (path), + "/dev/net/zone/%s/%s", zonename, provider); + } else { + (void) snprintf(path, sizeof (path), "/dev/net/%s", + provider); + } if ((*fd = open(path, oflags)) != -1) return (DLPI_SUCCESS); @@ -1118,7 +1162,7 @@ fallback: if ((*fd = open(path, oflags)) != -1) return (DLPI_SUCCESS); - return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR); + return (errno == ENOENT || errno == EISDIR ? DLPI_ENOLINK : DL_SYSERR); } /* @@ -1130,7 +1174,8 @@ i_dlpi_style1_open(dlpi_impl_t *dip) int retval, save_errno; int fd; - retval = i_dlpi_open(dip->dli_linkname, &fd, dip->dli_oflags, B_TRUE); + retval = i_dlpi_open(dip->dli_linkname, dip->dli_zonename, &fd, + dip->dli_oflags, B_TRUE); if (retval != DLPI_SUCCESS) return (retval); dip->dli_fd = fd; @@ -1153,7 +1198,8 @@ i_dlpi_style2_open(dlpi_impl_t *dip) int fd; int retval, save_errno; - retval = i_dlpi_open(dip->dli_provider, &fd, dip->dli_oflags, B_FALSE); + retval = i_dlpi_open(dip->dli_provider, dip->dli_zonename, &fd, + dip->dli_oflags, B_FALSE); if (retval != DLPI_SUCCESS) return (retval); dip->dli_fd = fd; @@ -1571,7 +1617,8 @@ static const char *libdlpi_errlist[] = { /* DLPI_ENOTENOTSUP */ "invalid DLPI notification type", /* DLPI_ENOTEINVAL */ "invalid DLPI notification id", /* DLPI_ENOTEIDINVAL */ - "DLPI_IPNETINFO not supported" /* DLPI_EIPNETINFONOTSUP */ + "DLPI_IPNETINFO not supported", /* DLPI_EIPNETINFONOTSUP */ + "invalid zone name" /* DLPI_EZONENAMEINVAL */ }; const char * diff --git a/usr/src/lib/libdlpi/common/libdlpi.h b/usr/src/lib/libdlpi/common/libdlpi.h index 993ac1b7a4..364413ee3a 100644 --- a/usr/src/lib/libdlpi/common/libdlpi.h +++ b/usr/src/lib/libdlpi/common/libdlpi.h @@ -93,6 +93,7 @@ enum { DLPI_ENOTENOTSUP, /* DLPI notification not supported by link */ DLPI_ENOTEIDINVAL, /* invalid DLPI notification id */ DLPI_EIPNETINFONOTSUP, /* DLPI_IPNETINFO not supported */ + DLPI_EZONENAMEINVAL, /* invalid zone name */ DLPI_ERRMAX /* Highest + 1 libdlpi error code */ }; @@ -184,6 +185,7 @@ typedef boolean_t dlpi_walkfunc_t(const char *, void *); extern void dlpi_walk(dlpi_walkfunc_t *, void *, uint_t); extern int dlpi_open(const char *, dlpi_handle_t *, uint_t); +extern int dlpi_open_zone(const char *, const char *, dlpi_handle_t *, uint_t); extern void dlpi_close(dlpi_handle_t); extern int dlpi_info(dlpi_handle_t, dlpi_info_t *, uint_t); extern int dlpi_bind(dlpi_handle_t, uint_t, uint_t *); diff --git a/usr/src/lib/libdlpi/common/libdlpi_impl.h b/usr/src/lib/libdlpi/common/libdlpi_impl.h index 70708ff5af..8969cce7cb 100644 --- a/usr/src/lib/libdlpi/common/libdlpi_impl.h +++ b/usr/src/lib/libdlpi/common/libdlpi_impl.h @@ -28,6 +28,7 @@ #include <libdlpi.h> #include <sys/sysmacros.h> +#include <sys/zone.h> #ifdef __cplusplus extern "C" { @@ -112,6 +113,8 @@ typedef struct dlpi_impl_s { /* full linkname including PPA */ char dli_provider[DLPI_LINKNAME_MAX]; /* only provider name */ + char dli_zonename[ZONENAME_MAX]; + /* optionally specified zone */ t_uscalar_t dli_style; /* style 1 or 2 */ uint_t dli_saplen; /* bound SAP length */ uint_t dli_sap; /* bound SAP value */ diff --git a/usr/src/lib/libdlpi/common/mapfile-vers b/usr/src/lib/libdlpi/common/mapfile-vers index ed3231dc92..c818e5e660 100644 --- a/usr/src/lib/libdlpi/common/mapfile-vers +++ b/usr/src/lib/libdlpi/common/mapfile-vers @@ -67,6 +67,11 @@ SYMBOL_VERSION SUNW_1.1 { # first release of libdlpi, Solaris 11 SYMBOL_VERSION SUNWprivate { global: + # + # dlpi_open_zone should be moved to a new public section once it is + # upstreamed into illumos-gate . + # + dlpi_open_zone; dlpi_parselink; dlpi_makelink; dlpi_style; diff --git a/usr/src/lib/libdtrace/Makefile.com b/usr/src/lib/libdtrace/Makefile.com index c896f6f7f8..217c66ae77 100644 --- a/usr/src/lib/libdtrace/Makefile.com +++ b/usr/src/lib/libdtrace/Makefile.com @@ -88,6 +88,7 @@ DLIBSRCS += \ io.d \ ip.d \ iscsit.d \ + mac.d \ net.d \ nfs.d \ nfssrv.d \ @@ -101,7 +102,8 @@ DLIBSRCS += \ sysevent.d \ tcp.d \ udp.d \ - unistd.d + unistd.d \ + vnd.d include ../../Makefile.lib @@ -114,6 +116,7 @@ CLEANFILES += dt_lex.c dt_grammar.c dt_grammar.h y.output CLEANFILES += ../common/procfs.sed ../common/procfs.d CLEANFILES += ../common/io.sed ../common/io.d CLEANFILES += ../common/ip.sed ../common/ip.d +CLEANFILES += ../common/mac.sed ../common/mac.d CLEANFILES += ../common/net.sed ../common/net.d CLEANFILES += ../common/errno.d ../common/signal.d CLEANFILES += ../common/dt_errtags.c ../common/dt_names.c @@ -138,7 +141,7 @@ CERRWARN += -_gcc=-Wno-switch SMATCH=off YYCFLAGS = -LDLIBS += -lgen -lproc -lrtld_db -lnsl -lsocket -lctf -lelf -lc +LDLIBS += -lgen -lproc -lrtld_db -lnsl -lsocket -lctf -lelf -lc -lzonecfg DRTILDLIBS = $(LDLIBS.lib) -lc LIBDAUDITLIBS = $(LDLIBS.lib) -lmapmalloc -lc -lproc @@ -204,6 +207,9 @@ pics/dt_lex.o pics/dt_grammar.o := CCVERBOSE = ../common/ip.d: ../common/ip.sed ../common/ip.d.in sed -f ../common/ip.sed < ../common/ip.d.in > $@ +../common/mac.d: ../common/mac.sed ../common/mac.d.in + sed -f ../common/mac.sed < ../common/mac.d.in > $@ + ../common/net.d: ../common/net.sed ../common/net.d.in sed -f ../common/net.sed < ../common/net.d.in > $@ diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h index 2681709483..6841eca4e6 100644 --- a/usr/src/lib/libdtrace/common/dt_impl.h +++ b/usr/src/lib/libdtrace/common/dt_impl.h @@ -267,6 +267,7 @@ struct dtrace_hdl { uint_t dt_droptags; /* boolean: set via -xdroptags */ uint_t dt_active; /* boolean: set once tracing is active */ uint_t dt_stopped; /* boolean: set once tracing is stopped */ + uint_t dt_optset; /* boolean: set once options have been set */ processorid_t dt_beganon; /* CPU that executed BEGIN probe (if any) */ processorid_t dt_endedon; /* CPU that executed END probe (if any) */ uint_t dt_oflags; /* dtrace open-time options (see dtrace.h) */ diff --git a/usr/src/lib/libdtrace/common/dt_module.c b/usr/src/lib/libdtrace/common/dt_module.c index cc4cdbaa19..be83e44938 100644 --- a/usr/src/lib/libdtrace/common/dt_module.c +++ b/usr/src/lib/libdtrace/common/dt_module.c @@ -1372,6 +1372,77 @@ dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr, return (0); } +/* + * We've been asked to look up something inside a pid related module and it has + * been qualified with a library name. In that case, we may have to split this + * up into the library and the type itself, which will be separated by an '`' + * character. This is complicated further by the fact that the keyword for a + * struct, union, or enum, will precede the library. Hence we may have something + * that looks like "struct libsocket.so.1`msghdr" in name and we need to + * transform that into "libsocket.so.1" and "struct msghdr". + */ +int +dtrace_lookup_fixup_pidtype(const char *name, char **libp, char **typep) +{ + int len, i; + char *split = NULL, *lib, *buf; + char *base; + char *keywords[] = { "struct ", "union ", "enum ", NULL }; + + if (name == NULL) + return (-1); + + *libp = NULL; + *typep = NULL; + buf = strdup(name); + if (buf == NULL) + return (-1); + + i = 0; + lib = buf; + while (keywords[i] != NULL) { + base = keywords[i]; + len = strlen(base); + if (strncmp(name, base, len) == 0) { + lib += len; + break; + } + i++; + } + + split = strchr(buf, '`'); + assert(split != NULL); + *split = '\0'; + split++; + if (lib == buf) { + *libp = strdup(buf); + *typep = strdup(split); + if (*libp == NULL || *typep == NULL) + goto err; + free(buf); + return (0); + } else { + assert(len > 0); + assert(base != NULL); + + *libp = strdup(lib); + if (*libp == NULL) + goto err; + if (asprintf(typep, "%s%s", base, split) == -1) + goto err; + free(buf); + return (0); + } + +err: + free(buf); + free(*libp); + *libp = NULL; + free(*typep); + *typep = NULL; + return (-1); +} + int dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name, dtrace_typeinfo_t *tip) @@ -1383,7 +1454,6 @@ dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name, uint_t n, i; int justone; ctf_file_t *fp; - char *buf, *p, *q; uint_t mask = 0; /* mask of dt_module flags to match */ uint_t bits = 0; /* flag bits that must be present */ @@ -1437,19 +1507,19 @@ dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name, id = ctf_lookup_by_name(dmp->dm_ctfp, name); fp = dmp->dm_ctfp; } else { - if ((p = strchr(name, '`')) != NULL) { - buf = strdup(name); - if (buf == NULL) + dt_dprintf("Trying to find userland type: %s\n", name); + if (strchr(name, '`') != NULL) { + char *lib, *type; + if (dtrace_lookup_fixup_pidtype(name, &lib, + &type) != 0) { return (dt_set_errno(dtp, EDT_NOMEM)); - p = strchr(buf, '`'); - if ((q = strchr(p + 1, '`')) != NULL) - p = q; - *p = '\0'; - fp = dt_module_getctflib(dtp, dmp, buf); + } + fp = dt_module_getctflib(dtp, dmp, lib); if (fp == NULL || (id = ctf_lookup_by_name(fp, - p + 1)) == CTF_ERR) + type)) == CTF_ERR) id = CTF_ERR; - free(buf); + free(lib); + free(type); } else { for (i = 0; i < dmp->dm_nctflibs; i++) { fp = dmp->dm_libctfp[i]; diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c index 3e3bf9768a..d515669e0c 100644 --- a/usr/src/lib/libdtrace/common/dt_open.c +++ b/usr/src/lib/libdtrace/common/dt_open.c @@ -40,6 +40,7 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <zone.h> #define _POSIX_PTHREAD_SEMANTICS #include <dirent.h> @@ -689,8 +690,8 @@ const dtrace_pattr_t _dtrace_prvdesc = { { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON }, }; -const char *_dtrace_defcpp = "/usr/ccs/lib/cpp"; /* default cpp(1) to invoke */ -const char *_dtrace_defld = "/usr/ccs/bin/ld"; /* default ld(1) to invoke */ +const char *_dtrace_defcpp = "/usr/lib/cpp"; /* default cpp(1) to invoke */ +const char *_dtrace_defld = "/usr/bin/ld"; /* default ld(1) to invoke */ const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */ const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */ @@ -830,6 +831,8 @@ dt_vopen(int version, int flags, int *errp, dt_provmod_t *provmod = NULL; int i, err; struct rlimit rl; + const char *zroot; + char *libpath = NULL; const dt_intrinsic_t *dinp; const dt_typedef_t *dtyp; @@ -962,11 +965,19 @@ alloc: dtp->dt_provs = calloc(dtp->dt_provbuckets, sizeof (dt_provider_t *)); dt_proc_init(dtp); dtp->dt_vmax = DT_VERS_LATEST; - dtp->dt_cpp_path = strdup(_dtrace_defcpp); + zroot = zone_get_nroot(); + if (zroot != NULL) { + (void) asprintf(&dtp->dt_ld_path, "%s/%s", zroot, + _dtrace_defld); + (void) asprintf(&dtp->dt_cpp_path, "%s/%s", zroot, + _dtrace_defcpp); + } else { + dtp->dt_ld_path = strdup(_dtrace_defld); + dtp->dt_cpp_path = strdup(_dtrace_defcpp); + } dtp->dt_cpp_argv = malloc(sizeof (char *)); dtp->dt_cpp_argc = 1; dtp->dt_cpp_args = 1; - dtp->dt_ld_path = strdup(_dtrace_defld); dtp->dt_provmod = provmod; dtp->dt_vector = vector; dtp->dt_varg = arg; @@ -1311,9 +1322,15 @@ alloc: * compile, and to provide better error reporting (because the full * reporting of compiler errors requires dtrace_open() to succeed). */ - if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0) + if (zroot != NULL) + (void) asprintf(&libpath, "%s/%s", zroot, _dtrace_libdir); + if (dtrace_setopt(dtp, "libdir", + libpath != NULL ? libpath : _dtrace_libdir) != 0) return (set_open_errno(dtp, errp, dtp->dt_errno)); + if (libpath != NULL) + free(libpath); + return (dtp); } diff --git a/usr/src/lib/libdtrace/common/dt_options.c b/usr/src/lib/libdtrace/common/dt_options.c index 66366c65b4..991f197304 100644 --- a/usr/src/lib/libdtrace/common/dt_options.c +++ b/usr/src/lib/libdtrace/common/dt_options.c @@ -41,6 +41,8 @@ #include <alloca.h> #include <errno.h> #include <fcntl.h> +#include <zone.h> +#include <libzonecfg.h> #include <dt_impl.h> #include <dt_string.h> @@ -854,6 +856,44 @@ dt_opt_bufresize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) return (0); } +/*ARGSUSED*/ +static int +dt_opt_zone(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) +{ + zoneid_t z, did; + + if (arg == NULL) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + + /* + * If the specified zone is currently running, we'll query the kernel + * for its debugger ID. If it doesn't appear to be running, we'll look + * for it for among all installed zones (thereby allowing a zdefs + * enabling against a halted zone). + */ + if ((z = getzoneidbyname(arg)) != -1) { + if (zone_getattr(z, ZONE_ATTR_DID, &did, sizeof (did)) < 0) + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + } else { + zone_dochandle_t handle; + + if ((handle = zonecfg_init_handle()) == NULL) + return (dt_set_errno(dtp, errno)); + + if (zonecfg_get_handle(arg, handle) != Z_OK) { + zonecfg_fini_handle(handle); + return (dt_set_errno(dtp, EDT_BADOPTVAL)); + } + + did = zonecfg_get_did(handle); + zonecfg_fini_handle(handle); + } + + dtp->dt_options[DTRACEOPT_ZONE] = did; + + return (0); +} + int dt_options_load(dtrace_hdl_t *dtp) { @@ -993,6 +1033,7 @@ static const dt_option_t _dtrace_rtoptions[] = { { "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE }, { "strsize", dt_opt_strsize, DTRACEOPT_STRSIZE }, { "ustackframes", dt_opt_runtime, DTRACEOPT_USTACKFRAMES }, + { "zone", dt_opt_zone, DTRACEOPT_ZONE }, { "temporal", dt_opt_runtime, DTRACEOPT_TEMPORAL }, { NULL } }; @@ -1073,9 +1114,41 @@ dtrace_setopt(dtrace_hdl_t *dtp, const char *opt, const char *val) if (dtp->dt_active) return (dt_set_errno(dtp, EDT_ACTIVE)); + /* + * If our options had been previously ioctl'd down, + * clear dt_optset to indicate that a run-time option + * has since been set. + */ + dtp->dt_optset = B_FALSE; + return (op->o_func(dtp, val, op->o_option)); } } return (dt_set_errno(dtp, EDT_BADOPTNAME)); } + +int +dtrace_setopts(dtrace_hdl_t *dtp) +{ + void *dof; + int err; + + if (dtp->dt_optset) + return (0); + + if ((dof = dtrace_getopt_dof(dtp)) == NULL) + return (-1); /* dt_errno has been set for us */ + + if ((err = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof)) == -1) + (void) dt_set_errno(dtp, errno); + + dtrace_dof_destroy(dtp, dof); + + if (err == -1) + return (-1); + + dtp->dt_optset = B_TRUE; + + return (0); +} diff --git a/usr/src/lib/libdtrace/common/dt_program.c b/usr/src/lib/libdtrace/common/dt_program.c index 7d725bd0af..e4f9d8dd1c 100644 --- a/usr/src/lib/libdtrace/common/dt_program.c +++ b/usr/src/lib/libdtrace/common/dt_program.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. * Copyright (c) 2011 by Delphix. All rights reserved. */ @@ -154,6 +155,14 @@ dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, void *dof; int n, err; + /* + * If we have not yet ioctl'd down our options DOF, we'll do that + * before enabling any probes (some options will affect which probes + * we match). + */ + if (dtrace_setopts(dtp) != 0) + return (-1); + dtrace_program_info(dtp, pgp, pip); if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL) diff --git a/usr/src/lib/libdtrace/common/dt_work.c b/usr/src/lib/libdtrace/common/dt_work.c index 97a7f62d69..c330394027 100644 --- a/usr/src/lib/libdtrace/common/dt_work.c +++ b/usr/src/lib/libdtrace/common/dt_work.c @@ -25,7 +25,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2011, Joyent, Inc. All rights reserved. + */ #include <dt_impl.h> #include <stddef.h> @@ -164,13 +166,22 @@ dtrace_status(dtrace_hdl_t *dtp) int dtrace_go(dtrace_hdl_t *dtp) { - void *dof; - int err; - if (dtp->dt_active) return (dt_set_errno(dtp, EINVAL)); /* + * In most cases, we will have already ioctl'd down our options DOF + * by this point -- but if a libdtrace does a dtrace_setopt() after + * calling dtrace_program_exec() but before calling dtrace_go(), + * dt_optset will be cleared and we need to ioctl down the options + * DOF now. + */ + if (dtrace_setopts(dtp) != 0 && + (dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL)) { + return (-1); + } + + /* * If a dtrace:::ERROR program and callback are registered, enable the * program before we start tracing. If this fails for a vector open * with ENOTTY, we permit dtrace_go() to succeed so that vector clients @@ -178,19 +189,10 @@ dtrace_go(dtrace_hdl_t *dtp) * though they do not provide support for the DTRACEIOC_ENABLE ioctl. */ if (dtp->dt_errprog != NULL && - dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && ( - dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL)) - return (-1); /* dt_errno has been set for us */ - - if ((dof = dtrace_getopt_dof(dtp)) == NULL) + dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && + (dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL)) return (-1); /* dt_errno has been set for us */ - err = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof); - dtrace_dof_destroy(dtp, dof); - - if (err == -1 && (errno != ENOTTY || dtp->dt_vector == NULL)) - return (dt_set_errno(dtp, errno)); - if (dt_ioctl(dtp, DTRACEIOC_GO, &dtp->dt_beganon) == -1) { if (errno == EACCES) return (dt_set_errno(dtp, EDT_DESTRUCTIVE)); diff --git a/usr/src/lib/libdtrace/common/dtrace.h b/usr/src/lib/libdtrace/common/dtrace.h index 293ab944af..dd591296aa 100644 --- a/usr/src/lib/libdtrace/common/dtrace.h +++ b/usr/src/lib/libdtrace/common/dtrace.h @@ -83,6 +83,7 @@ extern const char *dtrace_subrstr(dtrace_hdl_t *, int); extern int dtrace_setopt(dtrace_hdl_t *, const char *, const char *); extern int dtrace_getopt(dtrace_hdl_t *, const char *, dtrace_optval_t *); +extern int dtrace_setopts(dtrace_hdl_t *); extern void dtrace_update(dtrace_hdl_t *); extern int dtrace_ctlfd(dtrace_hdl_t *); diff --git a/usr/src/lib/libdtrace/common/mac.d.in b/usr/src/lib/libdtrace/common/mac.d.in new file mode 100644 index 0000000000..6263d51bdd --- /dev/null +++ b/usr/src/lib/libdtrace/common/mac.d.in @@ -0,0 +1,66 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#pragma D depends_on library ip.d + +inline int ETHERTYPE_PUP = @ETHERTYPE_PUP@; +inline int ETHERTYPE_802_MIN = @ETHERTYPE_802_MIN@; +inline int ETHERTYPE_IP = @ETHERTYPE_IP@; +inline int ETHERTYPE_ARP = @ETHERTYPE_ARP@; +inline int ETHERTYPE_REVARP = @ETHERTYPE_REVARP@; +inline int ETHERTYPE_AT = @ETHERTYPE_AT@; +inline int ETHERTYPE_AARP = @ETHERTYPE_AARP@; +inline int ETHERTYPE_VLAN = @ETHERTYPE_VLAN@; +inline int ETHERTYPE_IPV6 = @ETHERTYPE_IPV6@; +inline int ETHERTYPE_SLOW = @ETHERTYPE_SLOW@; +inline int ETHERTYPE_PPPOED = @ETHERTYPE_PPPOED@; +inline int ETHERTYPE_PPPOES = @ETHERTYPE_PPPOES@; +inline int ETHERTYPE_EAPOL = @ETHERTYPE_EAPOL@; +inline int ETHERTYPE_RSN_PREAUTH = @ETHERTYPE_RSN_PREAUTH@; +inline int ETHERTYPE_TRILL = @ETHERTYPE_TRILL@; +inline int ETHERTYPE_FCOE = @ETHERTYPE_FCOE@; +inline int ETHERTYPE_MAX = @ETHERTYPE_MAX@; + + +typedef struct etherinfo { + uint8_t eth_dst[6]; /* Destination MAC addr */ + uint8_t eth_src[6]; /* Source MAC addr */ + uint16_t eth_type; /* Ethertype */ + boolean_t eth_istagged; /* Is the VLAN tag present */ + uint8_t eth_priority; /* Priority tag */ + uint8_t eth_dei; /* drop eligible indicator */ + uint16_t eth_vlanid; /* VLAN ID */ + uintptr_t eth_header; /* Pointer to start of header */ + uintptr_t eth_mblk; /* Pointer to the mblk containing header */ +} etherinfo_t; + +#pragma D binding "1.12.1" translator +translator etherinfo_t < mblk_t *mp > { + eth_dst = mp->b_rptr; + eth_src = mp->b_rptr + 6; + eth_type = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ? + ntohs(*(uint16_t *)(mp->b_rptr + 16)) : + ntohs(*(uint16_t *)(mp->b_rptr + 12)); + eth_istagged = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ? + 1 : 0; + eth_priority = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ? + ntohs(*(uint16_t *)(mp->b_rptr + 14)) & 0xe000: 0; + eth_dei = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ? + ntohs(*(uint16_t *)(mp->b_rptr + 14)) & 0x1000: 0; + eth_vlanid = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ? + ntohs(*(uint16_t *)(mp->b_rptr + 14)) & 0x0fff: 0; + eth_header = (uintptr_t)mp->b_rptr; + eth_mblk = (uintptr_t)mp; +}; diff --git a/usr/src/lib/libdtrace/common/mac.sed.in b/usr/src/lib/libdtrace/common/mac.sed.in new file mode 100644 index 0000000000..00e149d000 --- /dev/null +++ b/usr/src/lib/libdtrace/common/mac.sed.in @@ -0,0 +1,45 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + + +/* + * This file is a sed script which is first preprocessed by cpp or cc -E to + * define a set of sed directives which replace #define tokens with their + * values. After preprocessing, the sed script is run over vnd.d.in to + * replace the #define tokens listed below to create the finished vnd.d. + * Refer to the rules in libdtrace/Makefile.com for more information. + */ + +#include <sys/ethernet.h> + +#define SED_REPLACE(x) s/#x/x/g + +SED_REPLACE(ETHERTYPE_PUP) +SED_REPLACE(ETHERTYPE_802_MIN) +SED_REPLACE(ETHERTYPE_IP) +SED_REPLACE(ETHERTYPE_ARP) +SED_REPLACE(ETHERTYPE_REVARP) +SED_REPLACE(ETHERTYPE_AT) +SED_REPLACE(ETHERTYPE_AARP) +SED_REPLACE(ETHERTYPE_VLAN) +SED_REPLACE(ETHERTYPE_IPV6) +SED_REPLACE(ETHERTYPE_SLOW) +SED_REPLACE(ETHERTYPE_PPPOED) +SED_REPLACE(ETHERTYPE_PPPOES) +SED_REPLACE(ETHERTYPE_EAPOL) +SED_REPLACE(ETHERTYPE_RSN_PREAUTH) +SED_REPLACE(ETHERTYPE_TRILL) +SED_REPLACE(ETHERTYPE_FCOE) +SED_REPLACE(ETHERTYPE_MAX) diff --git a/usr/src/lib/libdtrace/common/vnd.d b/usr/src/lib/libdtrace/common/vnd.d new file mode 100644 index 0000000000..356c412150 --- /dev/null +++ b/usr/src/lib/libdtrace/common/vnd.d @@ -0,0 +1,28 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + + +#pragma D depends_on module vnd +#pragma D depends_on provider vnd +#pragma D depends_on library ip.d +#pragma D depends_on library mac.d + +#pragma D binding "1.6.3" translator +translator ifinfo_t < vnd_str_t *vsp > { + if_name = vsp != NULL ? stringof(vsp->vns_dev->vdd_lname) : "<null>"; + if_local = 0; + if_ipstack = vsp != NULL ? vsp->vns_nsd->vpnd_nsid : 0; + if_addr = (uintptr_t)vsp; +}; diff --git a/usr/src/lib/libefi/common/crc32_efi.c b/usr/src/lib/libefi/common/crc32_efi.c index 1dba28ad72..73039f1478 100644 --- a/usr/src/lib/libefi/common/crc32_efi.c +++ b/usr/src/lib/libefi/common/crc32_efi.c @@ -23,8 +23,9 @@ * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2019, Joyent, Inc. + */ #include <sys/crc32.h> diff --git a/usr/src/lib/libefi/common/rdwr_efi.c b/usr/src/lib/libefi/common/rdwr_efi.c index afbc4c4f00..74f65378b1 100644 --- a/usr/src/lib/libefi/common/rdwr_efi.c +++ b/usr/src/lib/libefi/common/rdwr_efi.c @@ -132,12 +132,98 @@ int efi_debug = 0; extern unsigned int efi_crc32(const unsigned char *, unsigned int); static int efi_read(int, struct dk_gpt *); +/* + * In normal operation, libefi just passes everything down to the kernel driver + * (and - usually - cmlb), as that code needs to react to any partitioning + * changes by changing device nodes under /dev/?dsk/ and the like. + * + * However, if we are running against an un-labeled lofi device on an older + * version of illumos, these ioctl()s aren't emulated. This can be a problem if + * we're in a non-global zone, which doesn't support labeled lofi, and our + * kernel is downrev. + * + * In this case, we'll simply emulate the ioctl()s that libefi actually needs, + * except those for efi_type(). They basically boil down to simple reads and + * writes, though this does skip a bunch of error checking. + * + * As a final wrinkle, rather than rely on an updated libefi, smartos-live's + * format_image tool directly builds and uses this source. + */ +static int +do_ioctl(int fd, int cmd, void *arg) +{ + struct dk_cinfo cinfo; + struct dk_minfo minfo; + dk_efi_t *efi = arg; + int saved_errno; + size_t len; + int error; + + error = ioctl(fd, cmd, arg); + + saved_errno = errno; + + if (error != -1 || errno != ENOTTY || + ioctl(fd, DKIOCINFO, (caddr_t)&cinfo) != 0 || + strcmp(cinfo.dki_cname, "lofi") != 0 || + ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&minfo) != 0) { + errno = saved_errno; + return (error); + } + + switch (cmd) { + case DKIOCGMBOOT: + len = (size_t)pread(fd, arg, minfo.dki_lbsize, 0); + error = (len == minfo.dki_lbsize) ? 0 : -1; + break; + + case DKIOCSMBOOT: + len = (size_t)pwrite(fd, arg, minfo.dki_lbsize, 0); + error = (len == minfo.dki_lbsize) ? 0 : -1; + break; + + case DKIOCGETEFI: + len = (size_t)pread(fd, (caddr_t)(uintptr_t)efi->dki_data_64, + efi->dki_length, efi->dki_lba * minfo.dki_lbsize); + error = (len == efi->dki_length) ? 0 : -1; + break; + + case DKIOCSETEFI: + len = (size_t)pwrite(fd, (caddr_t)(uintptr_t)efi->dki_data_64, + efi->dki_length, efi->dki_lba * minfo.dki_lbsize); + error = (len == efi->dki_length) ? 0 : -1; + break; + + default: + errno = saved_errno; + break; + } + + if (error == 0) + errno = 0; + + return (error); +} + +static int +efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) +{ + void *data = dk_ioc->dki_data; + int error; + + dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data; + error = do_ioctl(fd, cmd, (void *)dk_ioc); + dk_ioc->dki_data = data; + + return (error); +} + static int read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize) { struct dk_minfo disk_info; - if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1) + if ((do_ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1) return (errno); *capacity = disk_info.dki_capacity; *lbsize = disk_info.dki_lbsize; @@ -196,6 +282,7 @@ efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc) "the maximum number of partitions supported is %lu\n", MAX_PARTS); } + errno = EINVAL; return (-1); } @@ -246,7 +333,7 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc) if ((mbr = calloc(1, lbsize)) == NULL) return (VT_ERROR); - if ((ioctl(fd, DKIOCGMBOOT, (caddr_t)mbr)) == -1) { + if ((do_ioctl(fd, DKIOCGMBOOT, (caddr_t)mbr)) == -1) { free(mbr); return (VT_ERROR); } @@ -305,19 +392,6 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc) } static int -efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) -{ - void *data = dk_ioc->dki_data; - int error; - - dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data; - error = ioctl(fd, cmd, (void *)dk_ioc); - dk_ioc->dki_data = data; - - return (error); -} - -static int check_label(int fd, dk_efi_t *dk_ioc) { efi_gpt_t *efi; @@ -380,7 +454,7 @@ efi_read(int fd, struct dk_gpt *vtoc) /* * get the partition number for this file descriptor. */ - if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { + if (do_ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { if (efi_debug) { (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); } @@ -404,7 +478,7 @@ efi_read(int fd, struct dk_gpt *vtoc) } /* get the LBA size */ - if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) { + if (do_ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) { if (efi_debug) { (void) fprintf(stderr, "assuming LBA 512 bytes %d\n", @@ -997,7 +1071,7 @@ efi_write(int fd, struct dk_gpt *vtoc) int nblocks; diskaddr_t lba_backup_gpt_hdr; - if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { + if (do_ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { if (efi_debug) (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); switch (errno) { @@ -1181,6 +1255,8 @@ efi_free(struct dk_gpt *ptr) * Input: File descriptor * Output: 1 if disk has an EFI label, or > 2TB with no VTOC or legacy MBR. * Otherwise 0. + * + * This always returns 0 for an un-labeled lofi device. */ int efi_type(int fd) diff --git a/usr/src/lib/libfakekernel/common/kstat.c b/usr/src/lib/libfakekernel/common/kstat.c index 849f0ddf0e..4b6d2baa08 100644 --- a/usr/src/lib/libfakekernel/common/kstat.c +++ b/usr/src/lib/libfakekernel/common/kstat.c @@ -12,6 +12,7 @@ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright 2017 RackTop Systems. + * Copyright 2019 Joyent, Inc. */ #include <sys/types.h> @@ -42,6 +43,11 @@ kstat_named_init(kstat_named_t *knp, const char *name, uchar_t type) /*ARGSUSED*/ void +kstat_named_setstr(kstat_named_t *knp, const char *src) +{} + +/*ARGSUSED*/ +void kstat_install(kstat_t *ksp) {} diff --git a/usr/src/lib/libfakekernel/common/mapfile-vers b/usr/src/lib/libfakekernel/common/mapfile-vers index c7f3ae1d1d..731f6801a5 100644 --- a/usr/src/lib/libfakekernel/common/mapfile-vers +++ b/usr/src/lib/libfakekernel/common/mapfile-vers @@ -157,6 +157,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { kstat_create; kstat_create_zone; kstat_named_init; + kstat_named_setstr; kstat_delete; kstat_install; kstat_waitq_enter; @@ -217,6 +218,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { taskq_dispatch_ent; taskq_empty; taskq_member; + taskq_empty; taskq_wait; taskq_wait_id; diff --git a/usr/src/lib/libfakekernel/common/sys/user.h b/usr/src/lib/libfakekernel/common/sys/user.h index f37548895c..190d7ca64b 100644 --- a/usr/src/lib/libfakekernel/common/sys/user.h +++ b/usr/src/lib/libfakekernel/common/sys/user.h @@ -31,6 +31,7 @@ extern "C" { struct exdata; #if defined(_KERNEL) || defined(_FAKE_KERNEL) || defined(_KMEMUSER) typedef struct uf_info uf_info_t; +typedef uint_t uf_entry_gen_t; #endif typedef struct user user_t; diff --git a/usr/src/lib/libfakekernel/common/taskq.c b/usr/src/lib/libfakekernel/common/taskq.c index e3922f6c37..6fe2b995a0 100644 --- a/usr/src/lib/libfakekernel/common/taskq.c +++ b/usr/src/lib/libfakekernel/common/taskq.c @@ -25,6 +25,7 @@ /* * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2017, Joyent, Inc. * Copyright 2017 RackTop Systems. * Copyright 2018, Joyent, Inc. */ diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_fs.c b/usr/src/lib/libgrubmgmt/common/libgrub_fs.c index aa5faa6470..92078bccee 100644 --- a/usr/src/lib/libgrubmgmt/common/libgrub_fs.c +++ b/usr/src/lib/libgrubmgmt/common/libgrub_fs.c @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2011 Joyent, Inc. All rights reserved. */ /* * Copyright 2013 Nexenta Systems, Inc. All rights reserved. diff --git a/usr/src/lib/libidspace/Makefile b/usr/src/lib/libidspace/Makefile new file mode 100644 index 0000000000..44640eeddc --- /dev/null +++ b/usr/src/lib/libidspace/Makefile @@ -0,0 +1,45 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.lib + +HDRS = libidspace.h +HDRDIR = common + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install: install_h $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libidspace/Makefile.com b/usr/src/lib/libidspace/Makefile.com new file mode 100644 index 0000000000..ef259c3d2f --- /dev/null +++ b/usr/src/lib/libidspace/Makefile.com @@ -0,0 +1,38 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY = libidspace.a +VERS = .1 +OBJECTS = id_space.o \ + libidspace.o +COMDIR = $(SRC)/common/idspace + +include ../../Makefile.lib + +SRCDIR = ../common +SRCS = ../../../common/idspace/id_space.c +LIBS = $(DYNLIB) + +LDLIBS += -lumem + +.KEEP_STATE: + +all: $(LIBS) + +include ../../Makefile.targ + +objs/%.o pics/%.o: $(COMDIR)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/libidspace/amd64/Makefile b/usr/src/lib/libidspace/amd64/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/libidspace/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libidspace/common/libidspace.c b/usr/src/lib/libidspace/common/libidspace.c new file mode 100644 index 0000000000..7a9f8acd67 --- /dev/null +++ b/usr/src/lib/libidspace/common/libidspace.c @@ -0,0 +1,25 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014, Joyent, Inc. + */ + +/* + * Wrappers around the common id_space code, for userland. + */ +#include <sys/id_space.h> + +id_t +id_alloc_specific(id_space_t *idp, id_t id) +{ + return (id_alloc_specific_nosleep(idp, id)); +} diff --git a/usr/src/lib/libidspace/common/libidspace.h b/usr/src/lib/libidspace/common/libidspace.h new file mode 100644 index 0000000000..bb8690f19c --- /dev/null +++ b/usr/src/lib/libidspace/common/libidspace.h @@ -0,0 +1,42 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#ifndef _LIBIDSPACE_H +#define _LIBIDSPACE_H + +/* + * libidspace public header + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +typedef struct id_space id_space_t; + +extern id_space_t *id_space_create(const char *, id_t, id_t); +extern void id_space_destroy(id_space_t *); +extern void id_space_extend(id_space_t *, id_t, id_t); +extern id_t id_alloc(id_space_t *); +extern id_t id_alloc_specific(id_space_t *, id_t); +extern void id_free(id_space_t *, id_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBIDSPACE_H */ diff --git a/usr/src/lib/libidspace/common/llib-lidspace b/usr/src/lib/libidspace/common/llib-lidspace new file mode 100644 index 0000000000..39c628da47 --- /dev/null +++ b/usr/src/lib/libidspace/common/llib-lidspace @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <libidspace.h> diff --git a/usr/src/lib/libidspace/common/mapfile-vers b/usr/src/lib/libidspace/common/mapfile-vers new file mode 100644 index 0000000000..61ae855ee0 --- /dev/null +++ b/usr/src/lib/libidspace/common/mapfile-vers @@ -0,0 +1,47 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOS_1.0 { # first release of libidspace + global: + id_alloc; + id_alloc_specific; + id_free; + id_space_create; + id_space_destroy; + id_space_extend; +}; + + +SYMBOL_VERSION ILLUMOSprivate { + local: + *; +}; + diff --git a/usr/src/lib/libidspace/i386/Makefile b/usr/src/lib/libidspace/i386/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/libidspace/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libidspace/sparc/Makefile b/usr/src/lib/libidspace/sparc/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/libidspace/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libidspace/sparcv9/Makefile b/usr/src/lib/libidspace/sparcv9/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/libidspace/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libinetutil/common/mapfile-vers b/usr/src/lib/libinetutil/common/mapfile-vers index a26d988990..c86fdd956d 100644 --- a/usr/src/lib/libinetutil/common/mapfile-vers +++ b/usr/src/lib/libinetutil/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2017 Joyent, Inc. # # diff --git a/usr/src/lib/libipadm/common/libipadm.c b/usr/src/lib/libipadm/common/libipadm.c index e384229c29..9d57e1f16d 100644 --- a/usr/src/lib/libipadm/common/libipadm.c +++ b/usr/src/lib/libipadm/common/libipadm.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Joyent, Inc. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2016, Chris Fraire <cfraire@me.com>. */ @@ -286,11 +287,19 @@ ipadm_close(ipadm_handle_t iph) boolean_t ipadm_check_auth(void) { + int uid; struct passwd pwd; char buf[NSS_BUFLEN_PASSWD]; + /* + * Branded zones may have different kinds of auth, but root always + * allowed. + */ + if ((uid = getuid()) == 0) + return (B_TRUE); + /* get the password entry for the given user ID */ - if (getpwuid_r(getuid(), &pwd, buf, sizeof (buf)) == NULL) + if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) return (B_FALSE); /* check for presence of given authorization */ @@ -899,9 +908,21 @@ ipadm_door_call(ipadm_handle_t iph, void *arg, size_t asize, void **rbufp, reopen: (void) pthread_mutex_lock(&iph->iph_lock); - /* The door descriptor is opened if it isn't already */ + /* + * The door descriptor is opened if it isn't already. + */ if (iph->iph_door_fd == -1) { - if ((iph->iph_door_fd = open(IPMGMT_DOOR, O_RDONLY)) < 0) { + char door[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); + + /* + * If this is a branded zone, make sure we use the "/native" + * prefix for the door path: + */ + (void) snprintf(door, sizeof (door), "%s%s", zroot != NULL ? + zroot : "", IPMGMT_DOOR); + + if ((iph->iph_door_fd = open(door, O_RDONLY)) < 0) { err = errno; (void) pthread_mutex_unlock(&iph->iph_lock); return (err); diff --git a/usr/src/lib/libipmi/common/ipmi_lancfg.c b/usr/src/lib/libipmi/common/ipmi_lancfg.c index 3e3ebc6e81..b324891e3f 100644 --- a/usr/src/lib/libipmi/common/ipmi_lancfg.c +++ b/usr/src/lib/libipmi/common/ipmi_lancfg.c @@ -22,6 +22,9 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2018, Joyent, Inc. + */ /* * Query and configure LAN interfaces over IPMI. This is done through the @@ -34,6 +37,7 @@ #include <strings.h> #include <libipmi.h> +#include <netinet/in.h> #include "ipmi_impl.h" @@ -65,10 +69,26 @@ typedef struct ipmi_cmd_lan_set_config { #define IPMI_LAN_PARAM_SUBNET_MASK 6 #define IPMI_LAN_PARAM_GATEWAY_ADDR 12 +/* VLAN/IPv6 parameters are currently only supported for GET operations */ +#define IPMI_LAN_PARAM_VLAN_ID 20 +#define IPMI_LAN_PARAM_IPVX_ENABLED 51 +#define IPMI_LAN_PARAM_IPV6_NUM_ADDRS 55 +#define IPMI_LAN_PARAM_IPV6_SADDR 56 +#define IPMI_LAN_PARAM_IPV6_DADDR 59 +#define IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG 64 +#define IPMI_LAN_PARAM_IPV6_STATIC_ROUTE1 65 +#define IPMI_LAN_PARAM_IPV6_STATIC_ROUTE2 68 +#define IPMI_LAN_PARAM_IPV6_NUM_DYN_ROUTES 72 +#define IPMI_LAN_PARAM_IPV6_DYN_ROUTE 73 + #define IPMI_LAN_SET_COMPLETE 0x0 #define IPMI_LAN_SET_INPROGRESS 0x1 #define IPMI_LAN_SET_COMMIT 0x2 +/* bitfield values of IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG param */ +#define IPMI_LAN_IPV6_STATIC_ROUTES_ENABLED 0x1 +#define IPMI_LAN_IPV6_DYNAMIC_ROUTES_ENABLED 0x2 + typedef struct ipmi_lan_entry { int ile_param; int ile_mask; @@ -78,7 +98,7 @@ typedef struct ipmi_lan_entry { size_t ile_len; } ipmi_lan_entry_t; -static ipmi_lan_entry_t ipmi_lan_table[] = { +static ipmi_lan_entry_t ipmi_lan_ipv4_table[] = { { IPMI_LAN_PARAM_IP_ADDR, IPMI_LAN_SET_IPADDR, 0, 0, offsetof(ipmi_lan_config_t, ilc_ipaddr), sizeof (uint32_t) }, { IPMI_LAN_PARAM_IP_SOURCE, IPMI_LAN_SET_IPADDR_SOURCE, 0, 0, @@ -91,8 +111,8 @@ static ipmi_lan_entry_t ipmi_lan_table[] = { offsetof(ipmi_lan_config_t, ilc_gateway_addr), sizeof (uint32_t) } }; -#define IPMI_LAN_NENTRIES \ - (sizeof (ipmi_lan_table) / sizeof (ipmi_lan_table[0])) +#define IPMI_LAN_IPV4_NENTRIES \ + (sizeof (ipmi_lan_ipv4_table) / sizeof (ipmi_lan_ipv4_table[0])) static int ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set, @@ -129,15 +149,44 @@ ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set, return (0); } +struct ipmi_lan_ipv6_addr { + uint8_t ipva_selector; + uint8_t ipva_source; + uint8_t ipva_addr[16]; + uint8_t ipva_prefixlen; + uint8_t ipva_status; +}; + +struct ipmi_lan_ipv6_numaddrs { + uint8_t inva_num_saddrs; + uint8_t inva_num_daddrs; + uint8_t inva_support; +}; + +struct ipmi_lan_vlan_cfg { + uint8_t ivla_vlanid_lower; + DECL_BITFIELD3( + ivla_vlanid_upper :4, + __reserved :3, + ivla_vlan_enable :1); +}; + int ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp) { - uint8_t set; - int i; + uint8_t set, enabled, route_cfg, ndynroutes = 0; + int i, j; ipmi_lan_entry_t *lep; + struct ipmi_lan_ipv6_numaddrs numaddrs = { 0 }; + struct ipmi_lan_ipv6_addr addrv6 = { 0 }; + struct ipmi_lan_vlan_cfg vlancfg = { 0 }; + struct in6_addr sroute1 = { 0 }, sroute2 = { 0 }, droute = { 0 }; + boolean_t found_addr = B_FALSE; + boolean_t stat_routes_enabled = B_FALSE, dyn_routes_enabled = B_FALSE; if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS, 0, 0, &set, sizeof (set)) != 0) + /* errno set */ return (-1); if (set & IPMI_LAN_SET_INPROGRESS) @@ -145,14 +194,186 @@ ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp) else cfgp->ilc_set_in_progress = B_FALSE; - for (i = 0; i < IPMI_LAN_NENTRIES; i++) { - lep = &ipmi_lan_table[i]; - if (ipmi_lan_get_param(ihp, channel, lep->ile_param, - lep->ile_set, lep->ile_block, - (char *)cfgp + lep->ile_offset, lep->ile_len) != 0) + /* + * First determine which IP addressing modes (IPv4/6) are enabled. On + * service processors that don't support a version of IPMI that is + * IPv6-aware, this parameter won't exist. If we fail to look it up + * then we'll assume that only IPv4 is enabled. + */ + if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPVX_ENABLED, 0, 0, + &enabled, sizeof (enabled)) != 0) { + cfgp->ilc_ipv4_enabled = B_TRUE; + cfgp->ilc_ipv6_enabled = B_FALSE; + } else { + switch (enabled) { + case 0: + cfgp->ilc_ipv4_enabled = B_TRUE; + cfgp->ilc_ipv6_enabled = B_FALSE; + break; + case 1: + cfgp->ilc_ipv4_enabled = B_FALSE; + cfgp->ilc_ipv6_enabled = B_TRUE; + break; + case 2: + cfgp->ilc_ipv4_enabled = B_TRUE; + cfgp->ilc_ipv6_enabled = B_TRUE; + break; + default: + return (ipmi_set_error(ihp, EIPMI_BAD_RESPONSE, NULL)); + } + } + + /* If IPv4 support is enabled, gather the current configuration. */ + if (cfgp->ilc_ipv4_enabled == B_TRUE) { + for (i = 0; i < IPMI_LAN_IPV4_NENTRIES; i++) { + lep = &ipmi_lan_ipv4_table[i]; + if (ipmi_lan_get_param(ihp, channel, lep->ile_param, + lep->ile_set, lep->ile_block, + (char *)cfgp + lep->ile_offset, lep->ile_len) != 0) + /* errno set */ + return (-1); + } + } + + /* Next check if VLAN is enabled, and if so, grab the VLAN ID. */ + if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_VLAN_ID, 0, + 0, &vlancfg, sizeof (struct ipmi_lan_vlan_cfg)) != 0) { + /* errno set */ + return (-1); + } + cfgp->ilc_vlan_enabled = vlancfg.ivla_vlan_enable; + if (cfgp->ilc_vlan_enabled == B_TRUE) { + cfgp->ilc_vlan_id = (vlancfg.ivla_vlanid_upper << 8) | + vlancfg.ivla_vlanid_lower; + } + + /* If IPv6 support isn't enabled, then we're all done here. */ + if (cfgp->ilc_ipv6_enabled != B_TRUE) + return (0); + + /* + * First check for a static address. If we can't find one, we'll look + * for a dynamic address. The spec allows for multiple IPv6 static and + * dynamic addresses to exist in various states. For simplicity, we + * will search for the first address that is configured and active. + */ + if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_NUM_ADDRS, 0, + 0, &numaddrs, sizeof (numaddrs)) != 0) { + /* errno set */ + return (-1); + } + + for (i = 0; i < numaddrs.inva_num_saddrs; i++) { + if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_SADDR, + i, 0, &addrv6, sizeof (addrv6)) == 0 && + addrv6.ipva_status == 0) { + found_addr = B_TRUE; + cfgp->ilc_ipv6_source = IPMI_LAN_SRC_STATIC; + break; + } + } + for (i = 0; found_addr == B_FALSE && i < numaddrs.inva_num_daddrs; + i++) { + if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_DADDR, + i, 0, &addrv6, sizeof (addrv6)) == 0 && + addrv6.ipva_status == 0) { + found_addr = B_TRUE; + cfgp->ilc_ipv6_source = IPMI_LAN_SRC_DHCP; + break; + } + } + + /* + * If we didn't find any active static or dynamic addresses, then + * while IPv6 support is enabled, no IPv6 interfaces have been + * configured. We reset ilc_ipv6_enabled back to false so that + * callers know that the other ilc_ipv6_* fields are not valid. + */ + if (found_addr != B_TRUE) { + cfgp->ilc_ipv6_enabled = B_FALSE; + return (0); + } + + (void) memcpy(cfgp->ilc_ipv6_addr, addrv6.ipva_addr, + sizeof (addrv6.ipva_addr)); + + /* + * For the case that static addressing was used for the SP IP then we + * need to get the IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG parameter to + * determine if static or dynamic routes are enabled (or both). + * + * If DHCP was used to assign the SP IP, then only dynamic route + * discovery is supported. + */ + if (cfgp->ilc_ipv6_source == IPMI_LAN_SRC_STATIC && + ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG, + 0, 0, &route_cfg, sizeof (route_cfg)) != 0) { + /* errno set */ + return (-1); + } + + if (cfgp->ilc_ipv6_source == IPMI_LAN_SRC_STATIC) { + if (route_cfg & IPMI_LAN_IPV6_STATIC_ROUTES_ENABLED) + stat_routes_enabled = B_TRUE; + if (route_cfg & IPMI_LAN_IPV6_DYNAMIC_ROUTES_ENABLED) + dyn_routes_enabled = B_TRUE; + } else { + dyn_routes_enabled = B_TRUE; + } + + /* + * The IPMI spec allows for a max of two static IPv6 routes to be + * configured. + */ + j = cfgp->ilc_ipv6_nroutes = 0; + if (stat_routes_enabled == B_TRUE) { + cfgp->ilc_ipv6_nroutes = 2; + if (ipmi_lan_get_param(ihp, channel, + IPMI_LAN_PARAM_IPV6_STATIC_ROUTE1, 0, 0, &sroute1, + sizeof (sroute1)) != 0 || + ipmi_lan_get_param(ihp, channel, + IPMI_LAN_PARAM_IPV6_STATIC_ROUTE2, 0, 0, &sroute1, + sizeof (sroute2)) != 0) { + /* errno set */ return (-1); + } + if (IN6_IS_ADDR_UNSPECIFIED(&sroute1)) { + cfgp->ilc_ipv6_nroutes++; + (void) memcpy(cfgp->ilc_ipv6_routes[j++], &sroute1, + sizeof (sroute1)); + } + if (IN6_IS_ADDR_UNSPECIFIED(&sroute2) != B_TRUE) { + cfgp->ilc_ipv6_nroutes++; + (void) memcpy(cfgp->ilc_ipv6_routes[j++], &sroute2, + sizeof (sroute2)); + } } + /* + * RFC4861 states that if dynamic routing is used, a host should retain + * a minimum of two routes, though more is recommended. Retrieve the + * number of dynamic routes and then iterate through them and gather + * up to the first two addresses. + */ + if (dyn_routes_enabled == B_TRUE && + ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_NUM_DYN_ROUTES, + 0, 0, &ndynroutes, sizeof (ndynroutes)) != 0) { + /* errno set */ + return (-1); + } + for (i = 0; i < ndynroutes && i < 2; i++) { + if (ipmi_lan_get_param(ihp, channel, + IPMI_LAN_PARAM_IPV6_DYN_ROUTE, i, 0, &droute, + sizeof (droute)) != 0) + /* errno set */ + return (-1); + + if (IN6_IS_ADDR_UNSPECIFIED(&droute) != B_TRUE) { + (void) memcpy(cfgp->ilc_ipv6_routes[j++], &droute, + sizeof (droute)); + cfgp->ilc_ipv6_nroutes++; + } + } return (0); } @@ -220,8 +441,8 @@ ipmi_lan_set_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp, /* * Iterate over all parameters and set them. */ - for (i = 0; i < IPMI_LAN_NENTRIES; i++) { - lep = &ipmi_lan_table[i]; + for (i = 0; i < IPMI_LAN_IPV4_NENTRIES; i++) { + lep = &ipmi_lan_ipv4_table[i]; if (!(lep->ile_mask & mask)) continue; diff --git a/usr/src/lib/libipmi/common/libipmi.h b/usr/src/lib/libipmi/common/libipmi.h index 1e69e57854..c3605616ed 100644 --- a/usr/src/lib/libipmi/common/libipmi.h +++ b/usr/src/lib/libipmi/common/libipmi.h @@ -297,6 +297,9 @@ extern ipmi_channel_info_t *ipmi_get_channel_info(ipmi_handle_t *, int); * This can be expanded in the future as needed. */ +/* We'll return up to a maximum of two static routee + two dynamic routes */ +#define IPMI_LAN_IPV6_MAX_ROUTES 4 + typedef struct ipmi_lan_config { boolean_t ilc_set_in_progress; uint32_t ilc_ipaddr; @@ -304,8 +307,17 @@ typedef struct ipmi_lan_config { uint8_t ilc_macaddr[6]; uint32_t ilc_subnet; uint32_t ilc_gateway_addr; + uint8_t ilc_ipv6_source; + uint8_t ilc_ipv6_addr[16]; + uint8_t ilc_ipv6_routes[IPMI_LAN_IPV6_MAX_ROUTES][16]; + uint8_t ilc_ipv6_nroutes; + uint16_t ilc_vlan_id; + boolean_t ilc_ipv4_enabled; + boolean_t ilc_ipv6_enabled; + boolean_t ilc_vlan_enabled; } ipmi_lan_config_t; +/* values for ilc_ipaddr_source */ #define IPMI_LAN_SRC_UNSPECIFIED 0x0 #define IPMI_LAN_SRC_STATIC 0x1 #define IPMI_LAN_SRC_DHCP 0x2 diff --git a/usr/src/lib/libjedec/common/llib-ljedec b/usr/src/lib/libjedec/common/llib-ljedec new file mode 100644 index 0000000000..332a64c742 --- /dev/null +++ b/usr/src/lib/libjedec/common/llib-ljedec @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2018, Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <libjedec.h> diff --git a/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com b/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com index 05fe0a1e2a..1ff4abafdd 100644 --- a/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com +++ b/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com @@ -36,10 +36,8 @@ KMFINC= -I../../../include -I../../../ber_der/inc BERLIB= -lkmf -lkmfberder BERLIB64= $(BERLIB) -OPENSSLLIBS= $(BERLIB) -lcrypto -lcryptoutil -lc -OPENSSLLIBS64= $(BERLIB64) -lcrypto -lcryptoutil -lc - -NATIVE_LIBS += libcrypto.so +OPENSSLLIBS= $(BERLIB) -lsunw_crypto -lcryptoutil -lc +OPENSSLLIBS64= $(BERLIB64) -lsunw_crypto -lcryptoutil -lc SRCDIR= ../common INCDIR= ../../include diff --git a/usr/src/lib/libnisdb/db_mindex3.cc b/usr/src/lib/libnisdb/db_mindex3.cc index 7f0d0c9449..66cdb10052 100644 --- a/usr/src/lib/libnisdb/db_mindex3.cc +++ b/usr/src/lib/libnisdb/db_mindex3.cc @@ -284,7 +284,7 @@ entriesFromLDAPthread(void *voidarg) { /* Lock to prevent removal */ (void) __nis_lock_db_table(arg->tableName, 1, 0, - "entriesFromLDAPthread"); + (char *)"entriesFromLDAPthread"); /* * It's possible that the db_mindex for the table has changed, @@ -316,7 +316,7 @@ entriesFromLDAPthread(void *voidarg) { (void) entriesFromLDAPreal(arg); (void) __nis_ulock_db_table(arg->tableName, 1, 0, - "entriesFromLDAPthread"); + (char *)"entriesFromLDAPthread"); freeQuery(arg->q); if (arg->dirObj != 0) diff --git a/usr/src/lib/libnisdb/db_table.cc b/usr/src/lib/libnisdb/db_table.cc index fe0fc88d23..37e78bd44b 100644 --- a/usr/src/lib/libnisdb/db_table.cc +++ b/usr/src/lib/libnisdb/db_table.cc @@ -602,7 +602,7 @@ db_table::setEntryExp(entryp where, entry_obj *obj, int initialLoad) { if (o != 0) { __nis_buffer_t b = {0, 0}; - bp2buf(myself, &b, "%s.%s", + bp2buf(myself, &b, (char *)"%s.%s", o->zo_name, o->zo_domain); t = getObjMapping(b.buf, 0, 1, 0, 0); sfree(b.buf); @@ -970,7 +970,7 @@ db_table::setEnumMode(long enumNum) { if (stat != DB_SUCCESS) { enumMode.flag = 0; enumCount.flag = 0; - logmsg(MSG_NOTIMECHECK, LOG_ERR, + logmsg(MSG_NOTIMECHECK, LOG_ERR, (char *) "%s: No memory for enum check array; entry removal disabled", myself); } diff --git a/usr/src/lib/libnisdb/nis_db.cc b/usr/src/lib/libnisdb/nis_db.cc index c2264bd5f7..9a49fac7dc 100644 --- a/usr/src/lib/libnisdb/nis_db.cc +++ b/usr/src/lib/libnisdb/nis_db.cc @@ -528,7 +528,7 @@ dbFindObject(char *objName, db_status *statP) { /* If not the root dir, find the directory where the entry lives */ sfree(table); - name = entryName(myself, objName, &table); + name = entryName((char *)myself, objName, &table); if (name == 0 || table == 0) { sfree(name); RETSTAT(0, DB_MEMORY_LIMIT); @@ -738,7 +738,7 @@ dbDeleteObj(char *objName) { nod->objType = o->zo_data.zo_type; nis_destroy_object(o); - nod->objName = sdup(myself, T, objName); + nod->objName = sdup((char *)myself, T, objName); if (nod->objName == 0) { sfree(nod); return (DB_MEMORY_LIMIT); @@ -790,7 +790,7 @@ dbTouchObj(char *objName) { sfree(table); table = 0; - ent = entryName(myself, objName, &table); + ent = entryName((char *)myself, objName, &table); if (ent == 0 || table == 0) { sfree(ent); return (DB_MEMORY_LIMIT); @@ -990,7 +990,7 @@ dbRefreshObj(char *name, nis_object *o) { int lstat; /* Find parent */ - ent = entryName(myself, objName, &table); + ent = entryName((char *)myself, objName, &table); if (ent == 0 || table == 0) { sfree(b.buf); sfree(objTable); diff --git a/usr/src/lib/libnsl/common/mapfile-vers b/usr/src/lib/libnsl/common/mapfile-vers index 0b12140f41..bae1af4edc 100644 --- a/usr/src/lib/libnsl/common/mapfile-vers +++ b/usr/src/lib/libnsl/common/mapfile-vers @@ -21,6 +21,7 @@ # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015 Joyent, Inc. # Copyright 2018 Nexenta Systems, Inc. # diff --git a/usr/src/lib/libnsl/netselect/netselect.c b/usr/src/lib/libnsl/netselect/netselect.c index 7225fa86a6..461840b695 100644 --- a/usr/src/lib/libnsl/netselect/netselect.c +++ b/usr/src/lib/libnsl/netselect/netselect.c @@ -22,6 +22,7 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -32,8 +33,6 @@ * under license from the Regents of the University of California. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "mt.h" #include "../rpc/rpc_mt.h" /* for MT declarations only */ #include <rpc/types.h> @@ -45,6 +44,7 @@ #include <malloc.h> #include <libintl.h> #include <syslog.h> +#include <zone.h> #include "netcspace.h" #define FAILURE (unsigned)(-1) @@ -265,13 +265,22 @@ freenetconfigent(struct netconfig *netp) static struct netconfig ** getnetlist(void) { - char line[BUFSIZ]; /* holds each line of NETCONFIG */ - FILE *fp; /* file stream for NETCONFIG */ + FILE *fp = NULL; /* file stream for NETCONFIG */ struct netconfig **listpp; /* the beginning of the netconfig list */ struct netconfig **tpp; /* used to traverse the netconfig list */ int count; /* the number of entries in file */ + char nc_path[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); + char line[BUFSIZ]; /* holds each line of NETCONFIG */ + + /* + * If we are running in a branded zone, ensure we use the "/native" + * prefix when opening the netconfig file: + */ + (void) snprintf(nc_path, sizeof (nc_path), "%s%s", zroot != NULL ? + zroot : "", NETCONFIG); - if ((fp = fopen(NETCONFIG, "rF")) == NULL) { + if ((fp = fopen(nc_path, "rF")) == NULL) { nc_error = NC_OPENFAIL; return (NULL); } @@ -286,13 +295,16 @@ getnetlist(void) if (count == 0) { nc_error = NC_NOTFOUND; - (void) fclose(fp); + if (fp != NULL) + (void) fclose(fp); return (NULL); } + if ((listpp = malloc((count + 1) * sizeof (struct netconfig *))) == NULL) { nc_error = NC_NOMEM; - (void) fclose(fp); + if (fp != NULL) + (void) fclose(fp); return (NULL); } @@ -311,6 +323,7 @@ getnetlist(void) if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */ netlist_free(&listpp); + return (listpp); } diff --git a/usr/src/lib/libnvpair/libnvpair.h b/usr/src/lib/libnvpair/libnvpair.h index b05669e506..197ec37f46 100644 --- a/usr/src/lib/libnvpair/libnvpair.h +++ b/usr/src/lib/libnvpair/libnvpair.h @@ -49,6 +49,8 @@ extern int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *, extern void nvlist_print(FILE *, nvlist_t *); extern int nvlist_print_json(FILE *, nvlist_t *); extern void dump_nvlist(nvlist_t *, int); +extern int nvlist_dump_json(nvlist_t *, char **); +extern void nvlist_dump_json_free(nvlist_t *, char *); /* * Private nvlist printing interface that allows the caller some control diff --git a/usr/src/lib/libnvpair/mapfile-vers b/usr/src/lib/libnvpair/mapfile-vers index 0403964e05..9b1f048f75 100644 --- a/usr/src/lib/libnvpair/mapfile-vers +++ b/usr/src/lib/libnvpair/mapfile-vers @@ -244,6 +244,8 @@ SYMBOL_VERSION SUNWprivate_1.1 { dump_nvlist; nvlist_add_hrtime; nvlist_lookup_hrtime; + nvlist_dump_json; + nvlist_dump_json_free; nvlist_print; nvlist_print_json; nvlist_prt; diff --git a/usr/src/lib/libnvpair/nvpair_json.c b/usr/src/lib/libnvpair/nvpair_json.c index 6aa3e9c01c..3205d229ab 100644 --- a/usr/src/lib/libnvpair/nvpair_json.c +++ b/usr/src/lib/libnvpair/nvpair_json.c @@ -18,16 +18,71 @@ #include <strings.h> #include <wchar.h> #include <sys/debug.h> +#include <stdarg.h> +#include <assert.h> #include "libnvpair.h" -#define FPRINTF(fp, ...) \ +#define FPRINTF(bufp, blen, offp, ...) \ do { \ - if (fprintf(fp, __VA_ARGS__) < 0) \ + if (nvlist_rasnprintf(bufp, blen, offp, \ + __VA_ARGS__) < 0) \ return (-1); \ } while (0) /* + * A realloc-aware snprintf/asprintf like function. + */ +/*PRINTFLIKE4*/ +static int +nvlist_rasnprintf(char **bufp, size_t *blen, off_t *boff, char *input, ...) +{ + int ret; + va_list ap; + size_t size; + char *b; + + if (*bufp == NULL) { + assert(*blen == 0); + assert(*boff == 0); + /* Pick a reasonable starting point, let's say 1k */ + *blen = 1024; + *bufp = malloc(*blen); + if (*bufp == NULL) + return (-1); + } + + size = *blen - *boff; + va_start(ap, input); + /* E_SEC_PRINTF_VAR_FMT */ + ret = vsnprintf(*bufp + *boff, size, input, ap); + va_end(ap); + if (ret < 0) + return (-1); + + if (ret >= size) { + size_t asize = *blen; + while (ret + *boff >= asize) + asize += 1024; + if ((b = realloc(*bufp, asize)) == NULL) + return (-1); + *bufp = b; + *blen = asize; + size = *blen - *boff; + va_start(ap, input); + /* E_SEC_PRINTF_VAR_FMT */ + ret = vsnprintf(*bufp + *boff, size, input, ap); + va_end(ap); + if (ret < 0) + return (-1); + assert(ret < size); + } + *boff += ret; + + return (0); +} + +/* * When formatting a string for JSON output we must escape certain characters, * as described in RFC4627. This applies to both member names and * DATA_TYPE_STRING values. @@ -44,7 +99,8 @@ * representable Unicode characters included in their escaped numeric form. */ static int -nvlist_print_json_string(FILE *fp, const char *input) +nvlist_print_json_string(const char *input, char **bufp, size_t *blen, + off_t *offp) { mbstate_t mbr; wchar_t c; @@ -52,29 +108,29 @@ nvlist_print_json_string(FILE *fp, const char *input) bzero(&mbr, sizeof (mbr)); - FPRINTF(fp, "\""); + FPRINTF(bufp, blen, offp, "\""); while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) { switch (c) { case '"': - FPRINTF(fp, "\\\""); + FPRINTF(bufp, blen, offp, "\\\""); break; case '\n': - FPRINTF(fp, "\\n"); + FPRINTF(bufp, blen, offp, "\\n"); break; case '\r': - FPRINTF(fp, "\\r"); + FPRINTF(bufp, blen, offp, "\\r"); break; case '\\': - FPRINTF(fp, "\\\\"); + FPRINTF(bufp, blen, offp, "\\\\"); break; case '\f': - FPRINTF(fp, "\\f"); + FPRINTF(bufp, blen, offp, "\\f"); break; case '\t': - FPRINTF(fp, "\\t"); + FPRINTF(bufp, blen, offp, "\\t"); break; case '\b': - FPRINTF(fp, "\\b"); + FPRINTF(bufp, blen, offp, "\\b"); break; default: if ((c >= 0x00 && c <= 0x1f) || @@ -84,13 +140,15 @@ nvlist_print_json_string(FILE *fp, const char *input) * characters in the Basic Multilingual Plane * as JSON-escaped multibyte characters. */ - FPRINTF(fp, "\\u%04x", (int)(0xffff & c)); + FPRINTF(bufp, blen, offp, "\\u%04x", + (int)(0xffff & c)); } else if (c >= 0x20 && c <= 0x7f) { /* * Render other 7-bit ASCII characters directly * and drop other, unrepresentable characters. */ - FPRINTF(fp, "%c", (int)(0xff & c)); + FPRINTF(bufp, blen, offp, "%c", + (int)(0xff & c)); } break; } @@ -105,98 +163,103 @@ nvlist_print_json_string(FILE *fp, const char *input) return (-1); } - FPRINTF(fp, "\""); + FPRINTF(bufp, blen, offp, "\""); return (0); } -/* - * Dump a JSON-formatted representation of an nvlist to the provided FILE *. - * This routine does not output any new-lines or additional whitespace other - * than that contained in strings, nor does it call fflush(3C). - */ -int -nvlist_print_json(FILE *fp, nvlist_t *nvl) +static int +nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp) { nvpair_t *curr; boolean_t first = B_TRUE; - FPRINTF(fp, "{"); + FPRINTF(bufp, blen, offp, "{"); for (curr = nvlist_next_nvpair(nvl, NULL); curr; curr = nvlist_next_nvpair(nvl, curr)) { data_type_t type = nvpair_type(curr); if (!first) - FPRINTF(fp, ","); + FPRINTF(bufp, blen, offp, ","); else first = B_FALSE; - if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1) + if (nvlist_print_json_string(nvpair_name(curr), bufp, blen, + offp) == -1) return (-1); - FPRINTF(fp, ":"); + FPRINTF(bufp, blen, offp, ":"); switch (type) { case DATA_TYPE_STRING: { char *string = fnvpair_value_string(curr); - if (nvlist_print_json_string(fp, string) == -1) + if (nvlist_print_json_string(string, bufp, blen, + offp) == -1) return (-1); break; } case DATA_TYPE_BOOLEAN: { - FPRINTF(fp, "true"); + FPRINTF(bufp, blen, offp, "true"); break; } case DATA_TYPE_BOOLEAN_VALUE: { - FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) == - B_TRUE ? "true" : "false"); + FPRINTF(bufp, blen, offp, "%s", + fnvpair_value_boolean_value(curr) == B_TRUE ? + "true" : "false"); break; } case DATA_TYPE_BYTE: { - FPRINTF(fp, "%hhu", fnvpair_value_byte(curr)); + FPRINTF(bufp, blen, offp, "%hhu", + fnvpair_value_byte(curr)); break; } case DATA_TYPE_INT8: { - FPRINTF(fp, "%hhd", fnvpair_value_int8(curr)); + FPRINTF(bufp, blen, offp, "%hhd", + fnvpair_value_int8(curr)); break; } case DATA_TYPE_UINT8: { - FPRINTF(fp, "%hhu", fnvpair_value_uint8_t(curr)); + FPRINTF(bufp, blen, offp, "%hhu", + fnvpair_value_uint8_t(curr)); break; } case DATA_TYPE_INT16: { - FPRINTF(fp, "%hd", fnvpair_value_int16(curr)); + FPRINTF(bufp, blen, offp, "%hd", + fnvpair_value_int16(curr)); break; } case DATA_TYPE_UINT16: { - FPRINTF(fp, "%hu", fnvpair_value_uint16(curr)); + FPRINTF(bufp, blen, offp, "%hu", + fnvpair_value_uint16(curr)); break; } case DATA_TYPE_INT32: { - FPRINTF(fp, "%d", fnvpair_value_int32(curr)); + FPRINTF(bufp, blen, offp, "%d", + fnvpair_value_int32(curr)); break; } case DATA_TYPE_UINT32: { - FPRINTF(fp, "%u", fnvpair_value_uint32(curr)); + FPRINTF(bufp, blen, offp, "%u", + fnvpair_value_uint32(curr)); break; } case DATA_TYPE_INT64: { - FPRINTF(fp, "%lld", + FPRINTF(bufp, blen, offp, "%lld", (long long)fnvpair_value_int64(curr)); break; } case DATA_TYPE_UINT64: { - FPRINTF(fp, "%llu", + FPRINTF(bufp, blen, offp, "%llu", (unsigned long long)fnvpair_value_uint64(curr)); break; } @@ -204,20 +267,21 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) case DATA_TYPE_HRTIME: { hrtime_t val; VERIFY0(nvpair_value_hrtime(curr, &val)); - FPRINTF(fp, "%llu", (unsigned long long)val); + FPRINTF(bufp, blen, offp, "%llu", + (unsigned long long)val); break; } case DATA_TYPE_DOUBLE: { double val; VERIFY0(nvpair_value_double(curr, &val)); - FPRINTF(fp, "%f", val); + FPRINTF(bufp, blen, offp, "%f", val); break; } case DATA_TYPE_NVLIST: { - if (nvlist_print_json(fp, - fnvpair_value_nvlist(curr)) == -1) + if (nvlist_do_json(fnvpair_value_nvlist(curr), bufp, + blen, offp) == -1) return (-1); break; } @@ -226,14 +290,15 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) char **val; uint_t valsz, i; VERIFY0(nvpair_value_string_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - if (nvlist_print_json_string(fp, val[i]) == -1) + FPRINTF(bufp, blen, offp, ","); + if (nvlist_print_json_string(val[i], bufp, + blen, offp) == -1) return (-1); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -241,14 +306,15 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) nvlist_t **val; uint_t valsz, i; VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - if (nvlist_print_json(fp, val[i]) == -1) + FPRINTF(bufp, blen, offp, ","); + if (nvlist_do_json(val[i], bufp, blen, + offp) == -1) return (-1); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -256,14 +322,14 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) boolean_t *val; uint_t valsz, i; VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, val[i] == B_TRUE ? + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, val[i] == B_TRUE ? "true" : "false"); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -271,13 +337,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) uchar_t *val; uint_t valsz, i; VERIFY0(nvpair_value_byte_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%hhu", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%hhu", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -285,13 +351,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) uint8_t *val; uint_t valsz, i; VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%hhu", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%hhu", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -299,13 +365,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) int8_t *val; uint_t valsz, i; VERIFY0(nvpair_value_int8_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%hd", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%hd", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -313,13 +379,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) uint16_t *val; uint_t valsz, i; VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%hu", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%hu", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -327,13 +393,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) int16_t *val; uint_t valsz, i; VERIFY0(nvpair_value_int16_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%hd", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%hd", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -341,13 +407,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) uint32_t *val; uint_t valsz, i; VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%u", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%u", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -355,13 +421,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) int32_t *val; uint_t valsz, i; VERIFY0(nvpair_value_int32_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%d", val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%d", val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -369,14 +435,14 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) uint64_t *val; uint_t valsz, i; VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%llu", + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%llu", (unsigned long long)val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -384,13 +450,14 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) int64_t *val; uint_t valsz, i; VERIFY0(nvpair_value_int64_array(curr, &val, &valsz)); - FPRINTF(fp, "["); + FPRINTF(bufp, blen, offp, "["); for (i = 0; i < valsz; i++) { if (i > 0) - FPRINTF(fp, ","); - FPRINTF(fp, "%lld", (long long)val[i]); + FPRINTF(bufp, blen, offp, ","); + FPRINTF(bufp, blen, offp, "%lld", + (long long)val[i]); } - FPRINTF(fp, "]"); + FPRINTF(bufp, blen, offp, "]"); break; } @@ -401,6 +468,41 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl) } - FPRINTF(fp, "}"); + FPRINTF(bufp, blen, offp, "}"); return (0); } + +int +nvlist_dump_json(nvlist_t *nvl, char **bufp) +{ + off_t off = 0; + size_t l = 0; + + *bufp = NULL; + return (nvlist_do_json(nvl, bufp, &l, &off)); +} + +/* ARGSUSED */ +void +nvlist_dump_json_free(nvlist_t *nvl, char *buf) +{ + free(buf); +} + +/* + * Dump a JSON-formatted representation of an nvlist to the provided FILE *. + * This routine does not output any new-lines or additional whitespace other + * than that contained in strings, nor does it call fflush(3C). + */ +int +nvlist_print_json(FILE *fp, nvlist_t *nvl) +{ + int ret; + char *buf; + + if ((ret = nvlist_dump_json(nvl, &buf)) < 0) + return (ret); + ret = fprintf(fp, "%s", buf); + nvlist_dump_json_free(nvl, buf); + return (ret); +} diff --git a/usr/src/lib/libppt/Makefile b/usr/src/lib/libppt/Makefile index d8f34163ab..7119d6715c 100644 --- a/usr/src/lib/libppt/Makefile +++ b/usr/src/lib/libppt/Makefile @@ -10,7 +10,7 @@ # # -# Copyright 2018 Joyent, Inc. +# Copyright 2019 Joyent, Inc. # include $(SRC)/lib/Makefile.lib @@ -20,6 +20,12 @@ SUBDIRS = $(MACH) $(BUILD64) $(MACH64) HDRS = libppt.h HDRDIR = common +ETCFILES= ppt_matches +ROOTETC= $(ROOT)/etc +IETCFILES= $(ETCFILES:%=$(ROOTETC)/%) + +$(IETCFILES) := FILEMODE = 0444 + all := TARGET= all clean := TARGET= clean clobber := TARGET= clobber @@ -33,11 +39,16 @@ install_h: $(ROOTHDRS) all install: install_h +install: $(IETCFILES) + check: $(CHECKHDRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) +$(ROOTETC)/%: % + $(INS.file) + FRC: include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libppt/common/llib-lppt b/usr/src/lib/libppt/common/llib-lppt new file mode 100644 index 0000000000..dadd992a31 --- /dev/null +++ b/usr/src/lib/libppt/common/llib-lppt @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <libppt.h> diff --git a/usr/src/lib/libppt/ppt_matches b/usr/src/lib/libppt/ppt_matches new file mode 100644 index 0000000000..03ff7e03ba --- /dev/null +++ b/usr/src/lib/libppt/ppt_matches @@ -0,0 +1 @@ +pci10de,15f0 diff --git a/usr/src/lib/libproc/common/Pcontrol.c b/usr/src/lib/libproc/common/Pcontrol.c index 5a135aef17..b68adfb044 100644 --- a/usr/src/lib/libproc/common/Pcontrol.c +++ b/usr/src/lib/libproc/common/Pcontrol.c @@ -348,10 +348,16 @@ static const ps_ops_t P_live_ops = { void _libproc_init(void) { + const char *root; + _libproc_debug = getenv("LIBPROC_DEBUG") != NULL; _libproc_no_qsort = getenv("LIBPROC_NO_QSORT") != NULL; _libproc_incore_elf = getenv("LIBPROC_INCORE_ELF") != NULL; + if ((root = zone_get_nroot()) != NULL) + (void) snprintf(procfs_path, sizeof (procfs_path), "%s/proc", + root); + (void) sigfillset(&blockable_sigs); (void) sigdelset(&blockable_sigs, SIGKILL); (void) sigdelset(&blockable_sigs, SIGSTOP); @@ -1793,6 +1799,9 @@ prldump(const char *caller, lwpstatus_t *lsp) case PR_SUSPENDED: dprintf("%s: SUSPENDED\n", caller); break; + case PR_BRAND: + dprintf("%s: BRANDPRIVATE (%d)\n", caller, lsp->pr_what); + break; default: dprintf("%s: Unknown\n", caller); break; @@ -1972,6 +1981,7 @@ Pstopstatus(struct ps_prochandle *P, case PR_FAULTED: case PR_JOBCONTROL: case PR_SUSPENDED: + case PR_BRAND: break; default: errno = EPROTO; @@ -3546,6 +3556,7 @@ Lstopstatus(struct ps_lwphandle *L, case PR_FAULTED: case PR_JOBCONTROL: case PR_SUSPENDED: + case PR_BRAND: break; default: errno = EPROTO; diff --git a/usr/src/lib/libproc/common/Pcontrol.h b/usr/src/lib/libproc/common/Pcontrol.h index 0eed5042a4..550b6779dd 100644 --- a/usr/src/lib/libproc/common/Pcontrol.h +++ b/usr/src/lib/libproc/common/Pcontrol.h @@ -126,6 +126,7 @@ typedef struct file_info { /* symbol information for a mapped file */ size_t file_shstrsz; /* section header string table size */ uintptr_t *file_saddrs; /* section header addresses */ uint_t file_nsaddrs; /* number of section header addresses */ + boolean_t file_cvt; /* Have we tried to convert this? */ } file_info_t; typedef struct map_info { /* description of an address space mapping */ diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c index f20f6f4f81..303755950d 100644 --- a/usr/src/lib/libproc/common/Psymtab.c +++ b/usr/src/lib/libproc/common/Psymtab.c @@ -744,6 +744,58 @@ Pname_to_loadobj(struct ps_prochandle *P, const char *name) return (Plmid_to_loadobj(P, PR_LMID_EVERY, name)); } +/* + * We've been given a file_info_t which doesn't have any CTF. However, it may + * have information that's in a format that we could convert if on the fly. + * We'll first try to convert the alternate debug file, if present, and then + * move onto the default file. The reason we prefer the alternate debug file is + * that if both exist, then it likely has any usable debugging information. + */ +static ctf_file_t * +Pconvert_file_ctf(file_info_t *fptr) +{ + int err; + ctf_file_t *fp; + char errmsg[1024]; + + /* + * Provide an opt in. + */ + if (getenv("LIBPROC_CTFCONVERT") == NULL) + return (NULL); + + /* + * If we've already attempted to call this, then that's it. No reason to + * pretend we'll be more successful again another time. + */ + if (fptr->file_cvt == B_TRUE) + return (NULL); + fptr->file_cvt = B_TRUE; + + fp = NULL; + if (fptr->file_dbgelf != NULL) { + fp = ctf_elfconvert(fptr->file_fd, fptr->file_dbgelf, NULL, 1, + 0, &err, errmsg, sizeof (errmsg)); + if (fp == NULL) { + dprintf("failed to convert %s: %s\n", fptr->file_pname, + err == ECTF_CONVBKERR ? errmsg : ctf_errmsg(err)); + } + } + if (fp == NULL) { + fp = ctf_elfconvert(fptr->file_fd, fptr->file_elf, NULL, 1, + 0, &err, errmsg, sizeof (errmsg)); + if (fp == NULL) { + dprintf("failed to convert %s: %s\n", fptr->file_pname, + err == ECTF_CONVBKERR ? errmsg : ctf_errmsg(err)); + } + } + if (fp != NULL) { + fptr->file_ctfp = fp; + } + + return (NULL); +} + ctf_file_t * Pbuild_file_ctf(struct ps_prochandle *P, file_info_t *fptr) { @@ -756,8 +808,9 @@ Pbuild_file_ctf(struct ps_prochandle *P, file_info_t *fptr) Pbuild_file_symtab(P, fptr); - if (fptr->file_ctf_size == 0) - return (NULL); + if (fptr->file_ctf_size == 0) { + return (Pconvert_file_ctf(fptr)); + } symp = fptr->file_ctf_dyn ? &fptr->file_dynsym : &fptr->file_symtab; if (symp->sym_data_pri == NULL) diff --git a/usr/src/lib/librefhash/Makefile b/usr/src/lib/librefhash/Makefile new file mode 100644 index 0000000000..b6853cb0c6 --- /dev/null +++ b/usr/src/lib/librefhash/Makefile @@ -0,0 +1,41 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install + +.KEEP_STATE: + +all clean clobber install: $(SUBDIRS) + +install: install_h $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/librefhash/Makefile.com b/usr/src/lib/librefhash/Makefile.com new file mode 100644 index 0000000000..1f914c46a6 --- /dev/null +++ b/usr/src/lib/librefhash/Makefile.com @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY = librefhash.a +VERS = .1 +OBJECTS = list.o \ + refhash.o +HASHCOMDIR = $(SRC)/common/refhash +LISTCOMDIR = $(SRC)/common/list + +include ../../Makefile.lib + +SRCDIR = ../common +SRCS = $(HASHCOMDIR)/refhash.c $(LISTCOMDIR)/list.c +LIBS = $(DYNLIB) + +LDLIBS += -lc -lumem + +.KEEP_STATE: + +all: $(LIBS) + +include ../../Makefile.targ + +objs/%.o pics/%.o: $(LISTCOMDIR)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +objs/%.o pics/%.o: $(HASHCOMDIR)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/librefhash/amd64/Makefile b/usr/src/lib/librefhash/amd64/Makefile new file mode 100644 index 0000000000..5a2ea08b45 --- /dev/null +++ b/usr/src/lib/librefhash/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/librefhash/common/mapfile-vers b/usr/src/lib/librefhash/common/mapfile-vers new file mode 100644 index 0000000000..f7f91ef84f --- /dev/null +++ b/usr/src/lib/librefhash/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOSprivate { + global: + refhash_create; + refhash_destroy; + refhash_insert; + refhash_remove; + refhash_lookup; + refhash_linear_search; + refhash_hold; + refhash_rele; + refhash_first; + refhash_next; + refhash_obj_valid; + local: + *; +}; + diff --git a/usr/src/lib/librefhash/i386/Makefile b/usr/src/lib/librefhash/i386/Makefile new file mode 100644 index 0000000000..aeec694b08 --- /dev/null +++ b/usr/src/lib/librefhash/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/librefhash/sparc/Makefile b/usr/src/lib/librefhash/sparc/Makefile new file mode 100644 index 0000000000..aeec694b08 --- /dev/null +++ b/usr/src/lib/librefhash/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/librefhash/sparcv9/Makefile b/usr/src/lib/librefhash/sparcv9/Makefile new file mode 100644 index 0000000000..5a2ea08b45 --- /dev/null +++ b/usr/src/lib/librefhash/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/librename/Makefile b/usr/src/lib/librename/Makefile new file mode 100644 index 0000000000..1278c6cc9d --- /dev/null +++ b/usr/src/lib/librename/Makefile @@ -0,0 +1,44 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.lib + +HDRS = librename.h +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber lint: $(SUBDIRS) + +install: $(SUBDIRS) install_h + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/librename/Makefile.com b/usr/src/lib/librename/Makefile.com new file mode 100644 index 0000000000..f0a22f25ac --- /dev/null +++ b/usr/src/lib/librename/Makefile.com @@ -0,0 +1,34 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +LIBRARY = librename.a +VERS = .1 +OBJECTS = librename.o \ + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc +CPPFLAGS += -I../common + +SRCDIR = ../common + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/librename/amd64/Makefile b/usr/src/lib/librename/amd64/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/librename/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/librename/common/librename.c b/usr/src/lib/librename/common/librename.c new file mode 100644 index 0000000000..1e198a50a3 --- /dev/null +++ b/usr/src/lib/librename/common/librename.c @@ -0,0 +1,229 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Implementation of librename(3RENAME) interfaces. + */ + +#include <librename.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <synch.h> + +typedef enum librename_atomic_state { + LIBRENAME_ATOMIC_INITIAL = 0x0, + LIBRENAME_ATOMIC_FSYNC, + LIBRENAME_ATOMIC_RENAME, + LIBRENAME_ATOMIC_POSTSYNC, + LIBRENAME_ATOMIC_COMPLETED +} librename_atomic_state_t; + +struct librename_atomic { + char *lra_fname; /* RO */ + char *lra_altname; /* RO */ + int lra_dirfd; /* RO */ + int lra_tmpfd; /* RO */ + mutex_t lra_lock; + librename_atomic_state_t lra_state; /* lra_lock */ +}; + +int +librename_atomic_fdinit(int fd, const char *file, const char *prefix, + int mode, int flags, librename_atomic_t **outp) +{ + int ret; + int oflags; + librename_atomic_t *lrap; + struct stat st; + + if (fd < 0 || file == NULL || outp == NULL) + return (EINVAL); + + if (flags & ~(LIBRENAME_ATOMIC_NOUNLINK | LIBRENAME_ATOMIC_CLOEXEC)) + return (EINVAL); + + if (strchr(file, '/') != NULL) + return (EINVAL); + + if (prefix != NULL && strchr(prefix, '/') != NULL) + return (EINVAL); + + *outp = NULL; + lrap = malloc(sizeof (librename_atomic_t)); + if (lrap == NULL) + return (errno); + + if (fstat(fd, &st) != 0) { + ret = errno; + free(lrap); + return (ret); + } + + if (!S_ISDIR(st.st_mode)) { + free(lrap); + return (ENOTDIR); + } + + if ((lrap->lra_dirfd = dup(fd)) == -1) { + ret = errno; + free(lrap); + return (ret); + } + + + lrap->lra_fname = strdup(file); + if (lrap->lra_fname == NULL) { + ret = errno; + if (close(lrap->lra_dirfd) != 0) + abort(); + free(lrap); + return (ret); + } + + if (prefix == NULL) { + ret = asprintf(&lrap->lra_altname, ".%d.%s", (int)getpid(), + file); + } else { + ret = asprintf(&lrap->lra_altname, "%s%s", prefix, file); + } + if (ret == -1) { + ret = errno; + free(lrap->lra_fname); + if (close(lrap->lra_dirfd) != 0) + abort(); + free(lrap); + return (errno); + } + + oflags = O_CREAT | O_TRUNC | O_RDWR | O_NOFOLLOW; + if (flags & LIBRENAME_ATOMIC_NOUNLINK) + oflags |= O_EXCL; + + if (flags & LIBRENAME_ATOMIC_CLOEXEC) + oflags |= O_CLOEXEC; + + lrap->lra_tmpfd = openat(lrap->lra_dirfd, lrap->lra_altname, + oflags, mode); + if (lrap->lra_tmpfd < 0) { + ret = errno; + free(lrap->lra_altname); + free(lrap->lra_fname); + if (close(lrap->lra_dirfd) != 0) + abort(); + free(lrap); + return (ret); + } + + if (mutex_init(&lrap->lra_lock, USYNC_THREAD, NULL) != 0) + abort(); + + lrap->lra_state = LIBRENAME_ATOMIC_INITIAL; + *outp = lrap; + return (0); +} + +int +librename_atomic_init(const char *dir, const char *file, const char *prefix, + int mode, int flags, librename_atomic_t **outp) +{ + int fd, ret; + + if ((fd = open(dir, O_RDONLY)) < 0) + return (errno); + + ret = librename_atomic_fdinit(fd, file, prefix, mode, flags, outp); + if (close(fd) != 0) + abort(); + + return (ret); +} + +int +librename_atomic_fd(librename_atomic_t *lrap) +{ + return (lrap->lra_tmpfd); +} + +/* + * To atomically commit a file, we need to go through and do the following: + * + * o fsync the source + * o run rename + * o fsync the source again and the directory. + */ +int +librename_atomic_commit(librename_atomic_t *lrap) +{ + int ret = 0; + + if (mutex_lock(&lrap->lra_lock) != 0) + abort(); + if (lrap->lra_state == LIBRENAME_ATOMIC_COMPLETED) { + ret = EINVAL; + goto out; + } + + if (fsync(lrap->lra_tmpfd) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_FSYNC; + + if (renameat(lrap->lra_dirfd, lrap->lra_altname, lrap->lra_dirfd, + lrap->lra_fname) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_RENAME; + + if (fsync(lrap->lra_tmpfd) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_POSTSYNC; + + if (fsync(lrap->lra_dirfd) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_COMPLETED; + +out: + if (mutex_unlock(&lrap->lra_lock) != 0) + abort(); + return (ret); +} + +void +librename_atomic_fini(librename_atomic_t *lrap) +{ + + free(lrap->lra_altname); + free(lrap->lra_fname); + if (close(lrap->lra_tmpfd) != 0) + abort(); + if (close(lrap->lra_dirfd) != 0) + abort(); + if (mutex_destroy(&lrap->lra_lock) != 0) + abort(); + free(lrap); +} diff --git a/usr/src/lib/librename/common/librename.h b/usr/src/lib/librename/common/librename.h new file mode 100644 index 0000000000..cb344f534c --- /dev/null +++ b/usr/src/lib/librename/common/librename.h @@ -0,0 +1,43 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#ifndef _LIBRENAME_H +#define _LIBRENAME_H + +/* + * librename(3RENAME) public interfaces + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct librename_atomic librename_atomic_t; + +#define LIBRENAME_ATOMIC_NOUNLINK 0x01 +#define LIBRENAME_ATOMIC_CLOEXEC 0x02 +extern int librename_atomic_init(const char *, const char *, const char *, + int, int, librename_atomic_t **); +extern int librename_atomic_fdinit(int, const char *, const char *, int, int, + librename_atomic_t **); +extern int librename_atomic_fd(librename_atomic_t *); +extern int librename_atomic_commit(librename_atomic_t *); +extern void librename_atomic_fini(librename_atomic_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBRENAME_H */ diff --git a/usr/src/lib/librename/common/llib-lrename b/usr/src/lib/librename/common/llib-lrename new file mode 100644 index 0000000000..6f1dd81a7b --- /dev/null +++ b/usr/src/lib/librename/common/llib-lrename @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <librename.h> diff --git a/usr/src/lib/librename/common/mapfile-vers b/usr/src/lib/librename/common/mapfile-vers new file mode 100644 index 0000000000..af98188b42 --- /dev/null +++ b/usr/src/lib/librename/common/mapfile-vers @@ -0,0 +1,41 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOSprivate { + global: + librename_atomic_commit; + librename_atomic_fd; + librename_atomic_fdinit; + librename_atomic_fini; + librename_atomic_init; + local: + *; +}; diff --git a/usr/src/lib/librename/i386/Makefile b/usr/src/lib/librename/i386/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/librename/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/librename/sparc/Makefile b/usr/src/lib/librename/sparc/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/librename/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/librename/sparcv9/Makefile b/usr/src/lib/librename/sparcv9/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/librename/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libresolv2_joy/Makefile b/usr/src/lib/libresolv2_joy/Makefile new file mode 100644 index 0000000000..052dfdd092 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/Makefile @@ -0,0 +1,76 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. +# + + +include ../../Makefile.master +include ../Makefile.lib + +$(ROOTSVCMETHOD) := FILEMODE = 0555 + +SUBDIRS= include $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint +_msg := TARGET= _msg + +LIBRARY= libresolv_joy.a +TEXT_DOMAIN= SUNW_OST_OSLIB +XGETFLAGS= -a +POFILE= $(LIBRARY:.a=.po) +POFILES= generic.po + +SED= sed +GREP= grep + +.KEEP_STATE: + +all clean clobber lint: $(SUBDIRS) + +install: $(SUBDIRS) + +check: $(CHECKHDRS) $(CHKMANIFEST) + +_msg: $(MSGDOMAIN) $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + +$(POFILE): $(POFILES) + $(RM) $@ + $(CAT) $(POFILES) > $@ + +$(POFILES): + $(RM) messages.po + $(XGETTEXT) $(XGETFLAGS) *.[ch]* */*.[ch]* + $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@ + $(RM) messages.po + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET); echo + +FRC: + diff --git a/usr/src/lib/libresolv2_joy/Makefile.com b/usr/src/lib/libresolv2_joy/Makefile.com new file mode 100644 index 0000000000..59eda28fed --- /dev/null +++ b/usr/src/lib/libresolv2_joy/Makefile.com @@ -0,0 +1,165 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2019 Joyent, Inc. + +LIBRARY= libresolv_joy.a +VERS= .2 + +BSDOBJS= daemon.o putenv.o strcasecmp.o strsep.o \ + ftruncate.o readv.o strdup.o strtoul.o \ + gettimeofday.o setenv.o strerror.o utimes.o \ + mktemp.o setitimer.o strpbrk.o writev.o + +DSTOBJS= dst_api.o support.o hmac_link.o + +# inet_addr, inet_pton, inet_ntop, and inet_ntoa removed due to overlap with +# libnsl +INETOBJS= inet_net_pton.o inet_neta.o inet_lnaof.o \ + inet_netof.o nsap_addr.o inet_makeaddr.o \ + inet_network.o inet_net_ntop.o inet_cidr_ntop.o \ + inet_cidr_pton.o inet_data.o + +# build only the IRS objects that the ISC libbind's make would +IRSTHROBJS= gethostent_r.o getnetent_r.o getnetgrent_r.o \ + getprotoent_r.o getservent_r.o +IRSOBJS= ${IRSTHROBJS} \ + dns.o dns_ho.o dns_nw.o dns_pr.o \ + dns_sv.o gai_strerror.o gen.o gen_ho.o \ + gen_ng.o gen_nw.o gen_pr.o gen_sv.o \ + getaddrinfo.o gethostent.o getnameinfo.o getnetent.o \ + getnetgrent.o getprotoent.o getservent.o hesiod.o \ + irp.o irp_ho.o irp_ng.o irp_nw.o \ + irp_pr.o irp_sv.o irpmarshall.o irs_data.o \ + lcl.o lcl_ho.o lcl_ng.o lcl_nw.o \ + lcl_pr.o lcl_sv.o nis.o nul_ng.o \ + util.o + +ISCOBJS= assertions.o base64.o bitncmp.o ctl_clnt.o \ + ctl_p.o ctl_srvr.o ev_connects.o ev_files.o \ + ev_streams.o ev_timers.o ev_waits.o eventlib.o \ + heap.o hex.o logging.o memcluster.o \ + movefile.o tree.o + +NAMESEROBJS= ns_date.o ns_name.o ns_netint.o ns_parse.o \ + ns_print.o ns_samedomain.o ns_sign.o ns_ttl.o \ + ns_verify.o ns_rdata.o ns_newmsg.o + +RESOLVOBJS= herror.o mtctxres.o res_comp.o res_data.o \ + res_debug.o res_findzonecut.o res_init.o \ + res_mkquery.o res_mkupdate.o res_query.o res_send.o \ + res_sendsigned.o res_update.o + +SUNWOBJS= sunw_mtctxres.o sunw_updrec.o sunw_wrappers.o + +OBJECTS= $(BSDOBJS) $(DSTOBJS) $(INETOBJS) $(IRSOBJS) $(ISCOBJS) \ + $(NAMESEROBJS) $(RESOLVOBJS) $(SUNWOBJS) + +# include library definitions +include ../../Makefile.lib + +# install this library in the root filesystem +include ../../Makefile.rootfs + +# CC -v complains about things we aren't going to change in the ISC code +CCVERBOSE= + +SRCDIR = ../common +SRCS= $(BSDOBJS:%.o=../common/bsd/%.c) \ + $(DSTOBJS:%.o=../common/dst/%.c) \ + $(INETOBJS:%.o=../common/inet/%.c) \ + $(IRSOBJS:%.o=../common/irs/%.c) \ + $(ISCOBJS:%.o=../common/isc/%.c) \ + $(NAMESEROBJS:%.o=../common/nameser/%.c) \ + $(RESOLVOBJS:%.o=../common/resolv/%.c) \ + $(SUNWOBJS:%.o=../common/sunw/%.c) + +LIBS = $(DYNLIB) $(LINTLIB) + +$(LINTLIB):= SRCS = ../common/llib-lresolv_joy + +# Local Libresolv definitions + +SOLCOMPAT = -Dsocket=_socket +CRYPTFLAGS= -DHMAC_MD5 -DUSE_MD5 + +LOCFLAGS += $(CRYPTFLAGS) +LOCFLAGS += -D_SYS_STREAM_H -D_REENTRANT -DSVR4 -DSUNW_OPTIONS \ + $(SOLCOMPAT) -I../include -I../../common/inc + +CPPFLAGS += $(LOCFLAGS) + +CERRWARN += -_gcc=-Wno-implicit-function-declaration + +# not linted +SMATCH=off + +DYNFLAGS += $(ZNODELETE) + +LDLIBS += -lsocket -lnsl -lc -lmd + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +# include library targets +include ../../Makefile.targ + +pics/%.o: ../common/bsd/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/dst/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/inet/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/irs/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/isc/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/nameser/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/resolv/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: ../common/sunw/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +# install rule for lint library target +$(ROOTLINTDIR)/%: ../common/% + $(INS.file) diff --git a/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE new file mode 100644 index 0000000000..719d77fea2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE @@ -0,0 +1,185 @@ + * Copyright (c) 1995-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM 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. + + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + + * 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 (c) 1983, 1990, 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. + +/* Copyright (C) RSA Data Security, Inc. created 1986-1987, 1990, + 1992-1994, 1996. This is an + unpublished work protected as such under copyright law. This work + contains proprietary, confidential, and trade secret information of + RSA Data Security, Inc. Use, disclosure or reproduction without the + express written authorization of RSA Data Security, Inc. is + prohibited. + */ + + DNSSAFE LICENSE TERMS + + This BIND software includes the DNSsafe software from RSA Data + Security, Inc., which is copyrighted software that can only be + distributed under the terms of this license agreement. + + The DNSsafe software cannot be used or distributed separately from the + BIND software. You only have the right to use it or distribute it as + a bundled, integrated product. + + The DNSsafe software can ONLY be used to provide authentication for + resource records in the Domain Name System, as specified in RFC 2065 + and successors. You cannot modify the BIND software to use the + DNSsafe software for other purposes, or to make its cryptographic + functions available to end-users for other uses. + + If you modify the DNSsafe software itself, you cannot modify its + documented API, and you must grant RSA Data Security the right to use, + modify, and distribute your modifications, including the right to use + any patents or other intellectual property that your modifications + depend upon. + + You must not remove, alter, or destroy any of RSA's copyright notices + or license information. When distributing the software to the Federal + Government, it must be licensed to them as "commercial computer + software" protected under 48 CFR 12.212 of the FAR, or 48 CFR + 227.7202.1 of the DFARS. + + You must not violate United States export control laws by distributing + the DNSsafe software or information about it, when such distribution + is prohibited by law. + + THE DNSSAFE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY + WHATSOEVER. RSA HAS NO OBLIGATION TO SUPPORT, CORRECT, UPDATE OR + MAINTAIN THE RSA SOFTWARE. RSA DISCLAIMS ALL WARRANTIES, EXPRESS, + IMPLIED OR STATUTORY, AS TO ANY MATTER WHATSOEVER, INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY RIGHTS. + + If you desire to use DNSsafe in ways that these terms do not permit, + please contact RSA Data Security, Inc., 100 Marine Parkway, Redwood + City, California 94065, USA, to discuss alternate licensing + arrangements. + + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE. + + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by WIDE Project and + * its contributors. + * 4. 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. diff --git a/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..67315d8f33 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +BIND SOFTWARE diff --git a/usr/src/lib/libresolv2_joy/amd64/Makefile b/usr/src/lib/libresolv2_joy/amd64/Makefile new file mode 100644 index 0000000000..2e8cdecf75 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libresolv2_joy/common/bsd/daemon.c b/usr/src/lib/libresolv2_joy/common/bsd/daemon.c new file mode 100644 index 0000000000..54ff83b753 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/daemon.c @@ -0,0 +1,81 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: daemon.c,v 1.2 2005/04/27 04:56:10 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Copyright (c) 1990, 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. + */ + +#include "port_before.h" + +#include <fcntl.h> +#include <paths.h> +#include <unistd.h> + +#include "port_after.h" + +#ifndef NEED_DAEMON +int __bind_daemon__; +#else + +int +daemon(int nochdir, int noclose) { + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close (fd); + } + return (0); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c b/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c new file mode 100644 index 0000000000..744d205ce7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c @@ -0,0 +1,60 @@ +/*! \file + * \brief + * ftruncate - set file size, BSD Style + * + * shortens or enlarges the file as neeeded + * uses some undocumented locking call. It is known to work on SCO unix, + * other vendors should try. + * The #error directive prevents unsupported OSes + */ + +#include "port_before.h" + +#if defined(M_UNIX) +#define OWN_FTRUNCATE +#include <stdio.h> +#ifdef _XOPEN_SOURCE +#undef _XOPEN_SOURCE +#endif +#ifdef _POSIX_SOURCE +#undef _POSIX_SOURCE +#endif + +#include <fcntl.h> + +#include "port_after.h" + +int +__ftruncate(int fd, long wantsize) { + long cursize; + + /* determine current file size */ + if ((cursize = lseek(fd, 0L, 2)) == -1) + return (-1); + + /* maybe lengthen... */ + if (cursize < wantsize) { + if (lseek(fd, wantsize - 1, 0) == -1 || + write(fd, "", 1) == -1) { + return (-1); + } + return (0); + } + + /* maybe shorten... */ + if (wantsize < cursize) { + struct flock fl; + + fl.l_whence = 0; + fl.l_len = 0; + fl.l_start = wantsize; + fl.l_type = F_WRLCK; + return (fcntl(fd, F_FREESP, &fl)); + } + return (0); +} +#endif + +#ifndef OWN_FTRUNCATE +int __bindcompat_ftruncate; +#endif diff --git a/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c b/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c new file mode 100644 index 0000000000..f5ff8c54f5 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c @@ -0,0 +1,58 @@ +#include "port_before.h" +#include <stdio.h> +#include <syslog.h> +#include <sys/time.h> +#include "port_after.h" + +#if !defined(NEED_GETTIMEOFDAY) +/*% + * gettimeofday() occasionally returns invalid tv_usec on some platforms. + */ +#define MILLION 1000000 +#undef gettimeofday + +int +isc__gettimeofday(struct timeval *tp, struct timezone *tzp) { + int res; + + res = gettimeofday(tp, tzp); + if (res < 0) + return (res); + if (tp == NULL) + return (res); + if (tp->tv_usec < 0) { + do { + tp->tv_usec += MILLION; + tp->tv_sec--; + } while (tp->tv_usec < 0); + goto log; + } else if (tp->tv_usec > MILLION) { + do { + tp->tv_usec -= MILLION; + tp->tv_sec++; + } while (tp->tv_usec > MILLION); + goto log; + } + return (res); + log: + syslog(LOG_ERR, "gettimeofday: tv_usec out of range\n"); + return (res); +} +#else +int +gettimeofday(struct timeval *tvp, struct _TIMEZONE *tzp) { + time_t clock, time(time_t *); + + if (time(&clock) == (time_t) -1) + return (-1); + if (tvp) { + tvp->tv_sec = clock; + tvp->tv_usec = 0; + } + if (tzp) { + tzp->tz_minuteswest = 0; + tzp->tz_dsttime = 0; + } + return (0); +} +#endif /*NEED_GETTIMEOFDAY*/ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c b/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c new file mode 100644 index 0000000000..2d41b9b0ee --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1987, 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> + +#include "port_after.h" + +#if (!defined(NEED_MKTEMP)) && (!defined(NEED_MKSTEMP)) +int __mktemp_unneeded__; +#else + +static int gettemp(char *path, int *doopen); + +#ifdef NEED_MKSTEMP +mkstemp(char *path) { + int fd; + + return (gettemp(path, &fd) ? fd : -1); +} +#endif + +#ifdef NEED_MKTEMP +char * +mktemp(char *path) { + return(gettemp(path, (int *)NULL) ? path : (char *)NULL); +} +#endif + +static int +gettemp(char *path, int *doopen) { + char *start, *trv; + struct stat sbuf; + u_int pid; + + pid = getpid(); + for (trv = path; *trv; ++trv); /*%< extra X's get set to 0's */ + while (*--trv == 'X') { + *trv = (pid % 10) + '0'; + pid /= 10; + } + + /* + * check the target directory; if you have six X's and it + * doesn't exist this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) { + if (trv <= path) + break; + if (*trv == '/') { + *trv = '\0'; + if (stat(path, &sbuf)) + return(0); + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return(0); + } + *trv = '/'; + break; + } + } + + for (;;) { + if (doopen) { + if ((*doopen = + open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) + return(1); + if (errno != EEXIST) + return(0); + } + else if (stat(path, &sbuf)) + return(errno == ENOENT ? 1 : 0); + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) { + if (!*trv) + return(0); + if (*trv == 'z') + *trv++ = 'a'; + else { + if (isdigit(*trv)) + *trv = 'a'; + else + ++*trv; + break; + } + } + } + /*NOTREACHED*/ +} + +#endif /*NEED_MKTEMP*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/putenv.c b/usr/src/lib/libresolv2_joy/common/bsd/putenv.c new file mode 100644 index 0000000000..de1b070de6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/putenv.c @@ -0,0 +1,23 @@ +#include "port_before.h" +#include "port_after.h" + +/*% + * To give a little credit to Sun, SGI, + * and many vendors in the SysV world. + */ + +#if !defined(NEED_PUTENV) +int __bindcompat_putenv; +#else +int +putenv(char *str) { + char *tmp; + + for (tmp = str; *tmp && (*tmp != '='); tmp++) + ; + + return (setenv(str, tmp, 1)); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/readv.c b/usr/src/lib/libresolv2_joy/common/bsd/readv.c new file mode 100644 index 0000000000..0a8ade0ec1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/readv.c @@ -0,0 +1,35 @@ +#include "port_before.h" + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include "port_after.h" + +#ifndef NEED_READV +int __bindcompat_readv; +#else + +int +__readv(fd, vp, vpcount) + int fd; + const struct iovec *vp; + int vpcount; +{ + int count = 0; + + while (vpcount-- > 0) { + int bytes = read(fd, vp->iov_base, vp->iov_len); + + if (bytes < 0) + return (-1); + count += bytes; + if (bytes != vp->iov_len) + break; + vp++; + } + return (count); +} +#endif /* NEED_READV */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/setenv.c b/usr/src/lib/libresolv2_joy/common/bsd/setenv.c new file mode 100644 index 0000000000..f9fdbb7be4 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/setenv.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1987, 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. + */ + +#include "port_before.h" + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "port_after.h" + +#if !defined(NEED_SETENV) +int __bindcompat_setenv; +#else + +extern char **environ; + +static char *findenv(const char *name, int *offset); + +/*% + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(const char *name, const char *value, int rewrite) { + extern char **environ; + static int alloced; /*%< if allocated space before */ + char *c; + int l_value, offset; + + if (*value == '=') /*%< no `=' in value */ + ++value; + l_value = strlen(value); + if ((c = findenv(name, &offset))) { /*%< find if already exists */ + if (!rewrite) + return (0); + if (strlen(c) >= l_value) { /*%< old larger; copy over */ + while (*c++ = *value++); + return (0); + } + } else { /*%< create new slot */ + int cnt; + char **p; + + for (p = environ, cnt = 0; *p; ++p, ++cnt); + if (alloced) { /*%< just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /*%< get new space */ + alloced = 1; /*%< copy old entries into it */ + p = malloc((size_t)(sizeof(char *) * (cnt + 2))); + if (!p) + return (-1); + memcpy(p, environ, cnt * sizeof(char *)); + environ = p; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (c = (char *)name; *c && *c != '='; ++c); /*%< no `=' in name */ + if (!(environ[offset] = /*%< name + `=' + value */ + malloc((size_t)((int)(c - name) + l_value + 2)))) + return (-1); + for (c = environ[offset]; (*c = *name++) && *c != '='; ++c); + for (*c++ = '='; *c++ = *value++;); + return (0); +} + +/*% + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(const char *name) { + char **p; + int offset; + + while (findenv(name, &offset)) /*%< if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; +} + +/*% + * findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + * + * This routine *should* be a static; don't use it. + */ +static char * +findenv(const char *name, int *offset) { + const char *np; + char **p, *c; + int len; + + if (name == NULL || environ == NULL) + return (NULL); + for (np = name; *np && *np != '='; ++np) + continue; + len = np - name; + for (p = environ; (c = *p) != NULL; ++p) + if (strncmp(c, name, len) == 0 && c[len] == '=') { + *offset = p - environ; + return (c + len + 1); + } + return (NULL); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c b/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c new file mode 100644 index 0000000000..d336ac80d9 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c @@ -0,0 +1,25 @@ +#include "port_before.h" + +#include <sys/time.h> + +#include "port_after.h" + +/*% + * Setitimer emulation routine. + */ +#ifndef NEED_SETITIMER +int __bindcompat_setitimer; +#else + +int +__setitimer(int which, const struct itimerval *value, + struct itimerval *ovalue) +{ + if (alarm(value->it_value.tv_sec) >= 0) + return (0); + else + return (-1); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c b/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c new file mode 100644 index 0000000000..adf76ae483 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1987, 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. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/cdefs.h> + +#include <string.h> + +#include "port_after.h" + +#ifndef NEED_STRCASECMP +int __strcasecmp_unneeded__; +#else + +/*% + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static const u_char charmap[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377 +}; + +int +strcasecmp(const char *s1, const char *s2) { + const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + while (cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return (0); + return (cm[*us1] - cm[*--us2]); +} + +int +strncasecmp(const char *s1, const char *s2, size_t n) { + if (n != 0) { + const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + do { + if (cm[*us1] != cm[*us2++]) + return (cm[*us1] - cm[*--us2]); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return (0); +} + +#endif /*NEED_STRCASECMP*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strdup.c b/usr/src/lib/libresolv2_joy/common/bsd/strdup.c new file mode 100644 index 0000000000..a8d31e9587 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strdup.c @@ -0,0 +1,20 @@ +#include "port_before.h" + +#include <stdlib.h> + +#include "port_after.h" + +#ifndef NEED_STRDUP +int __bind_strdup_unneeded; +#else +char * +strdup(const char *src) { + char *dst = malloc(strlen(src) + 1); + + if (dst) + strcpy(dst, src); + return (dst); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strerror.c b/usr/src/lib/libresolv2_joy/common/bsd/strerror.c new file mode 100644 index 0000000000..107215c798 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strerror.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 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. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/types.h> + +#include <string.h> + +#include "port_after.h" + +#ifndef NEED_STRERROR +int __strerror_unneeded__; +#else + +#ifdef USE_SYSERROR_LIST +extern int sys_nerr; +extern char *sys_errlist[]; +#endif + +const char * +isc_strerror(int num) { +#define UPREFIX "Unknown error: " + static char ebuf[40] = UPREFIX; /*%< 64-bit number + slop */ + u_int errnum; + char *p, *t; +#ifndef USE_SYSERROR_LIST + const char *ret; +#endif + char tmp[40]; + + errnum = num; /*%< convert to unsigned */ +#ifdef USE_SYSERROR_LIST + if (errnum < (u_int)sys_nerr) + return (sys_errlist[errnum]); +#else +#undef strerror + ret = strerror(num); /*%< call strerror() in libc */ + if (ret != NULL) + return(ret); +#endif + + /* Do this by hand, so we don't include stdio(3). */ + t = tmp; + do { + *t++ = "0123456789"[errnum % 10]; + } while (errnum /= 10); + for (p = ebuf + sizeof(UPREFIX) - 1;;) { + *p++ = *--t; + if (t <= tmp) + break; + } + return (ebuf); +} + +#endif /*NEED_STRERROR*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c b/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c new file mode 100644 index 0000000000..01e08dd5f4 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1985, 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. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/cdefs.h> + +#include <string.h> + +#include "port_after.h" + +#ifndef NEED_STRPBRK +int __strpbrk_unneeded__; +#else + +/*% + * Find the first occurrence in s1 of a character in s2 (excluding NUL). + */ +char * +strpbrk(const char *s1, const char *s2) { + const char *scanp; + int c, sc; + + while ((c = *s1++) != 0) { + for (scanp = s2; (sc = *scanp++) != 0;) + if (sc == c) + return ((char *)(s1 - 1)); + } + return (NULL); +} + +#endif /*NEED_STRPBRK*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strsep.c b/usr/src/lib/libresolv2_joy/common/bsd/strsep.c new file mode 100644 index 0000000000..0470733543 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strsep.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1990, 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. + */ + +#include "port_before.h" +#include <sys/cdefs.h> +#include <string.h> +#include <stdio.h> +#include "port_after.h" + +#ifndef NEED_STRSEP +int __strsep_unneeded__; +#else + +/*% + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(char **stringp, const char *delim) { + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +#endif /*NEED_STRSEP*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c b/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c new file mode 100644 index 0000000000..bc199708b7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1990, 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +#include "port_after.h" + +#ifndef NEED_STRTOUL +int __strtoul_unneeded__; +#else + +/*% + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +u_long +strtoul(const char *nptr, char **endptr, int base) { + const char *s = nptr; + u_long acc, cutoff; + int neg, c, any, cutlim; + + neg = 0; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *(const unsigned char *)s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (u_long)ULONG_MAX / (u_long)base; + cutlim = (u_long)ULONG_MAX % (u_long)base; + for (acc = 0, any = 0;; c = *(const unsigned char*)s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + DE_CONST((any ? s - 1 : nptr), *endptr); + return (acc); +} + +#endif /*NEED_STRTOUL*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/utimes.c b/usr/src/lib/libresolv2_joy/common/bsd/utimes.c new file mode 100644 index 0000000000..2f65cffe25 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/utimes.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <utime.h> + +#include "port_after.h" + +#ifndef NEED_UTIMES +int __bind_utimes_unneeded; +#else + +int +__utimes(char *filename, struct timeval *tvp) { + struct utimbuf utb; + + utb.actime = (time_t)tvp[0].tv_sec; + utb.modtime = (time_t)tvp[1].tv_sec; + return (utime(filename, &utb)); +} + +#endif /* NEED_UTIMES */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/bsd/writev.c b/usr/src/lib/libresolv2_joy/common/bsd/writev.c new file mode 100644 index 0000000000..086f93266b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/writev.c @@ -0,0 +1,85 @@ +#include "port_before.h" + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include "port_after.h" + +#ifndef NEED_WRITEV +int __bindcompat_writev; +#else + +#ifdef _CRAY +#define OWN_WRITEV +int +__writev(int fd, struct iovec *iov, int iovlen) +{ + struct stat statbuf; + + if (fstat(fd, &statbuf) < 0) + return (-1); + + /* + * Allow for atomic writes to network. + */ + if (statbuf.st_mode & S_IFSOCK) { + struct msghdr mesg; + + memset(&mesg, 0, sizeof(mesg)); + mesg.msg_name = 0; + mesg.msg_namelen = 0; + mesg.msg_iov = iov; + mesg.msg_iovlen = iovlen; + mesg.msg_accrights = 0; + mesg.msg_accrightslen = 0; + return (sendmsg(fd, &mesg, 0)); + } else { + struct iovec *tv; + int i, rcode = 0, count = 0; + + for (i = 0, tv = iov; i <= iovlen; tv++) { + rcode = write(fd, tv->iov_base, tv->iov_len); + + if (rcode < 0) + break; + + count += rcode; + } + + if (count == 0) + return (rcode); + else + return (count); + } +} + +#else /*_CRAY*/ + +int +__writev(fd, vp, vpcount) + int fd; + const struct iovec *vp; + int vpcount; +{ + int count = 0; + + while (vpcount-- > 0) { + int written = write(fd, vp->iov_base, vp->iov_len); + + if (written < 0) + return (-1); + count += written; + if (written != vp->iov_len) + break; + vp++; + } + return (count); +} + +#endif /*_CRAY*/ + +#endif /*NEED_WRITEV*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/dst/dst_api.c b/usr/src/lib/libresolv2_joy/common/dst/dst_api.c new file mode 100644 index 0000000000..7215772ce2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/dst_api.c @@ -0,0 +1,1044 @@ +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE. + */ +/* + * This file contains the interface between the DST API and the crypto API. + * This is the only file that needs to be changed if the crypto system is + * changed. Exported functions are: + * void dst_init() Initialize the toolkit + * int dst_check_algorithm() Function to determines if alg is suppored. + * int dst_compare_keys() Function to compare two keys for equality. + * int dst_sign_data() Incremental signing routine. + * int dst_verify_data() Incremental verify routine. + * int dst_generate_key() Function to generate new KEY + * DST_KEY *dst_read_key() Function to retrieve private/public KEY. + * void dst_write_key() Function to write out a key. + * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST + * KEY structure. + * int dst_key_to_dnskey() Function to return a public key in DNS + * format binary + * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY + * int *dst_key_to_buffer() Writes out DST_KEY key matterial in buffer + * void dst_free_key() Releases all memory referenced by key structure + */ + +#include "port_before.h" +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <memory.h> +#include <ctype.h> +#include <time.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include "dst_internal.h" +#include "port_after.h" + +/* static variables */ +static int done_init = 0; +dst_func *dst_t_func[DST_MAX_ALGS]; +const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n"; +const char *dst_path = ""; + +/* internal I/O functions */ +static DST_KEY *dst_s_read_public_key(const char *in_name, + const u_int16_t in_id, int in_alg); +static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, + u_int16_t in_id, int in_alg); +static int dst_s_write_public_key(const DST_KEY *key); +static int dst_s_write_private_key(const DST_KEY *key); + +/* internal function to set up data structure */ +static DST_KEY *dst_s_get_key_struct(const char *name, const int alg, + const int flags, const int protocol, + const int bits); + +/*% + * dst_init + * This function initializes the Digital Signature Toolkit. + * Right now, it just checks the DSTKEYPATH environment variable. + * Parameters + * none + * Returns + * none + */ +void +dst_init() +{ + char *s; + int len; + + if (done_init != 0) + return; + done_init = 1; + + s = getenv("DSTKEYPATH"); + len = 0; + if (s) { + struct stat statbuf; + + len = strlen(s); + if (len > PATH_MAX) { + EREPORT(("%s is longer than %d characters, ignoring\n", + s, PATH_MAX)); + } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { + EREPORT(("%s is not a valid directory\n", s)); + } else { + char *tmp; + tmp = (char *) malloc(len + 2); + memcpy(tmp, s, len + 1); + if (tmp[strlen(tmp) - 1] != '/') { + tmp[strlen(tmp) + 1] = 0; + tmp[strlen(tmp)] = '/'; + } + dst_path = tmp; + } + } + memset(dst_t_func, 0, sizeof(dst_t_func)); + /* first one is selected */ + dst_hmac_md5_init(); +} + +/*% + * dst_check_algorithm + * This function determines if the crypto system for the specified + * algorithm is present. + * Parameters + * alg 1 KEY_RSA + * 3 KEY_DSA + * 157 KEY_HMAC_MD5 + * future algorithms TBD and registered with IANA. + * Returns + * 1 - The algorithm is available. + * 0 - The algorithm is not available. + */ +int +dst_check_algorithm(const int alg) +{ + return (dst_t_func[alg] != NULL); +} + +/*% + * dst_s_get_key_struct + * This function allocates key structure and fills in some of the + * fields of the structure. + * Parameters: + * name: the name of the key + * alg: the algorithm number + * flags: the dns flags of the key + * protocol: the dns protocol of the key + * bits: the size of the key + * Returns: + * NULL if error + * valid pointer otherwise + */ +static DST_KEY * +dst_s_get_key_struct(const char *name, const int alg, const int flags, + const int protocol, const int bits) +{ + DST_KEY *new_key = NULL; + + if (dst_check_algorithm(alg)) /*%< make sure alg is available */ + new_key = (DST_KEY *) malloc(sizeof(*new_key)); + if (new_key == NULL) + return (NULL); + + memset(new_key, 0, sizeof(*new_key)); + new_key->dk_key_name = strdup(name); + if (new_key->dk_key_name == NULL) { + free(new_key); + return (NULL); + } + new_key->dk_alg = alg; + new_key->dk_flags = flags; + new_key->dk_proto = protocol; + new_key->dk_KEY_struct = NULL; + new_key->dk_key_size = bits; + new_key->dk_func = dst_t_func[alg]; + return (new_key); +} + +/*% + * dst_compare_keys + * Compares two keys for equality. + * Parameters + * key1, key2 Two keys to be compared. + * Returns + * 0 The keys are equal. + * non-zero The keys are not equal. + */ + +int +dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2) +{ + if (key1 == key2) + return (0); + if (key1 == NULL || key2 == NULL) + return (4); + if (key1->dk_alg != key2->dk_alg) + return (1); + if (key1->dk_key_size != key2->dk_key_size) + return (2); + if (key1->dk_id != key2->dk_id) + return (3); + return (key1->dk_func->compare(key1, key2)); +} + +/*% + * dst_sign_data + * An incremental signing function. Data is signed in steps. + * First the context must be initialized (SIG_MODE_INIT). + * Then data is hashed (SIG_MODE_UPDATE). Finally the signature + * itself is created (SIG_MODE_FINAL). This function can be called + * once with INIT, UPDATE and FINAL modes all set, or it can be + * called separately with a different mode set for each step. The + * UPDATE step can be repeated. + * Parameters + * mode A bit mask used to specify operation(s) to be performed. + * SIG_MODE_INIT 1 Initialize digest + * SIG_MODE_UPDATE 2 Add data to digest + * SIG_MODE_FINAL 4 Generate signature + * from signature + * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL + * data Data to be signed. + * len The length in bytes of data to be signed. + * in_key Contains a private key to sign with. + * KEY structures should be handled (created, converted, + * compared, stored, freed) by the DST. + * signature + * The location to which the signature will be written. + * sig_len Length of the signature field in bytes. + * Return + * 0 Successfull INIT or Update operation + * >0 success FINAL (sign) operation + * <0 failure + */ + +int +dst_sign_data(const int mode, DST_KEY *in_key, void **context, + const u_char *data, const int len, + u_char *signature, const int sig_len) +{ + DUMP(data, mode, len, "dst_sign_data()"); + + if (mode & SIG_MODE_FINAL && + (in_key->dk_KEY_struct == NULL || signature == NULL)) + return (MISSING_KEY_OR_SIGNATURE); + + if (in_key->dk_func && in_key->dk_func->sign) + return (in_key->dk_func->sign(mode, in_key, context, data, len, + signature, sig_len)); + return (UNKNOWN_KEYALG); +} + +/*% + * dst_verify_data + * An incremental verify function. Data is verified in steps. + * First the context must be initialized (SIG_MODE_INIT). + * Then data is hashed (SIG_MODE_UPDATE). Finally the signature + * is verified (SIG_MODE_FINAL). This function can be called + * once with INIT, UPDATE and FINAL modes all set, or it can be + * called separately with a different mode set for each step. The + * UPDATE step can be repeated. + * Parameters + * mode Operations to perform this time. + * SIG_MODE_INIT 1 Initialize digest + * SIG_MODE_UPDATE 2 add data to digest + * SIG_MODE_FINAL 4 verify signature + * SIG_MODE_ALL + * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL) + * data Data to pass through the hash function. + * len Length of the data in bytes. + * in_key Key for verification. + * signature Location of signature. + * sig_len Length of the signature in bytes. + * Returns + * 0 Verify success + * Non-Zero Verify Failure + */ + +int +dst_verify_data(const int mode, DST_KEY *in_key, void **context, + const u_char *data, const int len, + const u_char *signature, const int sig_len) +{ + DUMP(data, mode, len, "dst_verify_data()"); + if (mode & SIG_MODE_FINAL && + (in_key->dk_KEY_struct == NULL || signature == NULL)) + return (MISSING_KEY_OR_SIGNATURE); + + if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL) + return (UNSUPPORTED_KEYALG); + return (in_key->dk_func->verify(mode, in_key, context, data, len, + signature, sig_len)); +} + +/*% + * dst_read_private_key + * Access a private key. First the list of private keys that have + * already been read in is searched, then the key accessed on disk. + * If the private key can be found, it is returned. If the key cannot + * be found, a null pointer is returned. The options specify required + * key characteristics. If the private key requested does not have + * these characteristics, it will not be read. + * Parameters + * in_keyname The private key name. + * in_id The id of the private key. + * options DST_FORCE_READ Read from disk - don't use a previously + * read key. + * DST_CAN_SIGN The key must be useable for signing. + * DST_NO_AUTHEN The key must be useable for authentication. + * DST_STANDARD Return any key + * Returns + * NULL If there is no key found in the current directory or + * this key has not been loaded before. + * !NULL Success - KEY structure returned. + */ + +DST_KEY * +dst_read_key(const char *in_keyname, const u_int16_t in_id, + const int in_alg, const int type) +{ + char keyname[PATH_MAX]; + DST_KEY *dg_key = NULL, *pubkey = NULL; + + if (!dst_check_algorithm(in_alg)) { /*%< make sure alg is available */ + EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n", + in_alg)); + return (NULL); + } + if ((type & (DST_PUBLIC | DST_PRIVATE)) == 0) + return (NULL); + if (in_keyname == NULL) { + EREPORT(("dst_read_private_key(): Null key name passed in\n")); + return (NULL); + } else if (strlen(in_keyname) >= sizeof(keyname)) { + EREPORT(("dst_read_private_key(): keyname too big\n")); + return (NULL); + } else + strcpy(keyname, in_keyname); + + /* before I read in the public key, check if it is allowed to sign */ + if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL) + return (NULL); + + if (type == DST_PUBLIC) + return pubkey; + + if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg, + pubkey->dk_flags, pubkey->dk_proto, + 0))) + return (dg_key); + /* Fill in private key and some fields in the general key structure */ + if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id, + pubkey->dk_alg) == 0) + dg_key = dst_free_key(dg_key); + + (void)dst_free_key(pubkey); + return (dg_key); +} + +int +dst_write_key(const DST_KEY *key, const int type) +{ + int pub = 0, priv = 0; + + if (key == NULL) + return (0); + if (!dst_check_algorithm(key->dk_alg)) { /*%< make sure alg is available */ + EREPORT(("dst_write_key(): Algorithm %d not suppored\n", + key->dk_alg)); + return (UNSUPPORTED_KEYALG); + } + if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0) + return (0); + + if (type & DST_PUBLIC) + if ((pub = dst_s_write_public_key(key)) < 0) + return (pub); + if (type & DST_PRIVATE) + if ((priv = dst_s_write_private_key(key)) < 0) + return (priv); + return (priv+pub); +} + +/*% + * dst_write_private_key + * Write a private key to disk. The filename will be of the form: + * K<key->dk_name>+<key->dk_alg+><key-d>k_id.><private key suffix>. + * If there is already a file with this name, an error is returned. + * + * Parameters + * key A DST managed key structure that contains + * all information needed about a key. + * Return + * >= 0 Correct behavior. Returns length of encoded key value + * written to disk. + * < 0 error. + */ + +static int +dst_s_write_private_key(const DST_KEY *key) +{ + u_char encoded_block[RAW_KEY_SIZE]; + char file[PATH_MAX]; + int len; + FILE *fp; + + /* First encode the key into the portable key format */ + if (key == NULL) + return (-1); + if (key->dk_KEY_struct == NULL) + return (0); /*%< null key has no private key */ + if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) { + EREPORT(("dst_write_private_key(): Unsupported operation %d\n", + key->dk_alg)); + return (-5); + } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block, + sizeof(encoded_block))) <= 0) { + EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len)); + return (-8); + } + /* Now I can create the file I want to use */ + dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg, + PRIVATE_KEY, PATH_MAX); + + /* Do not overwrite an existing file */ + if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) { + int nn; + if ((nn = fwrite(encoded_block, 1, len, fp)) != len) { + EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n", + file, len, nn, errno)); + fclose(fp); + return (-5); + } + fclose(fp); + } else { + EREPORT(("dst_write_private_key(): Can not create file %s\n" + ,file)); + return (-6); + } + memset(encoded_block, 0, len); + return (len); +} + +/*% +* + * dst_read_public_key + * Read a public key from disk and store in a DST key structure. + * Parameters + * in_name K<in_name><in_id>.<public key suffix> is the + * filename of the key file to be read. + * Returns + * NULL If the key does not exist or no name is supplied. + * NON-NULL Initialized key structure if the key exists. + */ + +static DST_KEY * +dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg) +{ + int flags, proto, alg, len, dlen; + int c; + char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace; + u_char deckey[RAW_KEY_SIZE]; + FILE *fp; + + if (in_name == NULL) { + EREPORT(("dst_read_public_key(): No key name given\n")); + return (NULL); + } + if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY, + PATH_MAX) == -1) { + EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n", + in_name, in_id, PUBLIC_KEY)); + return (NULL); + } + /* + * Open the file and read it's formatted contents up to key + * File format: + * domain.name [ttl] [IN] KEY <flags> <protocol> <algorithm> <key> + * flags, proto, alg stored as decimal (or hex numbers FIXME). + * (FIXME: handle parentheses for line continuation.) + */ + if ((fp = dst_s_fopen(name, "r", 0)) == NULL) { + EREPORT(("dst_read_public_key(): Public Key not found %s\n", + name)); + return (NULL); + } + /* Skip domain name, which ends at first blank */ + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + /* Skip blank to get to next field */ + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + + /* Skip optional TTL -- if initial digit, skip whole word. */ + if (isdigit(c)) { + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + } + /* Skip optional "IN" */ + if (c == 'I' || c == 'i') { + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + } + /* Locate and skip "KEY" */ + if (c != 'K' && c != 'k') { + EREPORT(("\"KEY\" doesn't appear in file: %s", name)); + return NULL; + } + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + ungetc(c, fp); /*%< return the charcter to the input field */ + /* Handle hex!! FIXME. */ + + if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) { + EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n" + ,name)); + return (NULL); + } + /* read in the key string */ + fgets(enckey, sizeof(enckey), fp); + + /* If we aren't at end-of-file, something is wrong. */ + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + if (!feof(fp)) { + EREPORT(("Key too long in file: %s", name)); + return NULL; + } + fclose(fp); + + if ((len = strlen(enckey)) <= 0) + return (NULL); + + /* discard \n */ + enckey[--len] = '\0'; + + /* remove leading spaces */ + for (notspace = (char *) enckey; isspace((*notspace)&0xff); len--) + notspace++; + + dlen = b64_pton(notspace, deckey, sizeof(deckey)); + if (dlen < 0) { + EREPORT(("dst_read_public_key: bad return from b64_pton = %d", + dlen)); + return (NULL); + } + /* store key and info in a key structure that is returned */ +/* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey, + dlen);*/ + return dst_buffer_to_key(in_name, alg, flags, proto, deckey, dlen); +} + +/*% + * dst_write_public_key + * Write a key to disk in DNS format. + * Parameters + * key Pointer to a DST key structure. + * Returns + * 0 Failure + * 1 Success + */ + +static int +dst_s_write_public_key(const DST_KEY *key) +{ + FILE *fp; + char filename[PATH_MAX]; + u_char out_key[RAW_KEY_SIZE]; + char enc_key[RAW_KEY_SIZE]; + int len = 0; + int mode; + + memset(out_key, 0, sizeof(out_key)); + if (key == NULL) { + EREPORT(("dst_write_public_key(): No key specified \n")); + return (0); + } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0) + return (0); + + /* Make the filename */ + if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id, + key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) { + EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n", + key->dk_key_name, key->dk_id, PUBLIC_KEY)); + return (0); + } + /* XXX in general this should be a check for symmetric keys */ + mode = (key->dk_alg == KEY_HMAC_MD5) ? 0600 : 0644; + /* create public key file */ + if ((fp = dst_s_fopen(filename, "w+", mode)) == NULL) { + EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n", + filename, errno)); + return (0); + } + /*write out key first base64 the key data */ + if (key->dk_flags & DST_EXTEND_FLAG) + b64_ntop(&out_key[6], len - 6, enc_key, sizeof(enc_key)); + else + b64_ntop(&out_key[4], len - 4, enc_key, sizeof(enc_key)); + fprintf(fp, "%s IN KEY %d %d %d %s\n", + key->dk_key_name, + key->dk_flags, key->dk_proto, key->dk_alg, enc_key); + fclose(fp); + return (1); +} + +/*% + * dst_dnskey_to_public_key + * This function converts the contents of a DNS KEY RR into a DST + * key structure. + * Paramters + * len Length of the RDATA of the KEY RR RDATA + * rdata A pointer to the the KEY RR RDATA. + * in_name Key name to be stored in key structure. + * Returns + * NULL Failure + * NON-NULL Success. Pointer to key structure. + * Caller's responsibility to free() it. + */ + +DST_KEY * +dst_dnskey_to_key(const char *in_name, const u_char *rdata, const int len) +{ + DST_KEY *key_st; + int alg ; + int start = DST_KEY_START; + + if (rdata == NULL || len <= DST_KEY_ALG) /*%< no data */ + return (NULL); + alg = (u_int8_t) rdata[DST_KEY_ALG]; + if (!dst_check_algorithm(alg)) { /*%< make sure alg is available */ + EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n", + alg)); + return (NULL); + } + + if (in_name == NULL) + return (NULL); + + if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL) + return (NULL); + + key_st->dk_id = dst_s_dns_key_id(rdata, len); + key_st->dk_flags = dst_s_get_int16(rdata); + key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT]; + if (key_st->dk_flags & DST_EXTEND_FLAG) { + u_int32_t ext_flags; + ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]); + key_st->dk_flags = key_st->dk_flags | (ext_flags << 16); + start += 2; + } + /* + * now point to the begining of the data representing the encoding + * of the key + */ + if (key_st->dk_func && key_st->dk_func->from_dns_key) { + if (key_st->dk_func->from_dns_key(key_st, &rdata[start], + len - start) > 0) + return (key_st); + } else + EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n", + alg)); + + SAFE_FREE(key_st); + return (key_st); +} + +/*% + * dst_public_key_to_dnskey + * Function to encode a public key into DNS KEY wire format + * Parameters + * key Key structure to encode. + * out_storage Location to write the encoded key to. + * out_len Size of the output array. + * Returns + * <0 Failure + * >=0 Number of bytes written to out_storage + */ + +int +dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage, + const int out_len) +{ + u_int16_t val; + int loc = 0; + int enc_len = 0; + if (key == NULL) + return (-1); + + if (!dst_check_algorithm(key->dk_alg)) { /*%< make sure alg is available */ + EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n", + key->dk_alg)); + return (UNSUPPORTED_KEYALG); + } + memset(out_storage, 0, out_len); + val = (u_int16_t)(key->dk_flags & 0xffff); + dst_s_put_int16(out_storage, val); + loc += 2; + + out_storage[loc++] = (u_char) key->dk_proto; + out_storage[loc++] = (u_char) key->dk_alg; + + if (key->dk_flags > 0xffff) { /*%< Extended flags */ + val = (u_int16_t)((key->dk_flags >> 16) & 0xffff); + dst_s_put_int16(&out_storage[loc], val); + loc += 2; + } + if (key->dk_KEY_struct == NULL) + return (loc); + if (key->dk_func && key->dk_func->to_dns_key) { + enc_len = key->dk_func->to_dns_key(key, + (u_char *) &out_storage[loc], + out_len - loc); + if (enc_len > 0) + return (enc_len + loc); + else + return (-1); + } else + EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n", + key->dk_alg)); + return (-1); +} + +/*% + * dst_buffer_to_key + * Function to encode a string of raw data into a DST key + * Parameters + * alg The algorithm (HMAC only) + * key A pointer to the data + * keylen The length of the data + * Returns + * NULL an error occurred + * NON-NULL the DST key + */ +DST_KEY * +dst_buffer_to_key(const char *key_name, /*!< name of the key */ + const int alg, /*!< algorithm */ + const int flags, /*!< dns flags */ + const int protocol, /*!< dns protocol */ + const u_char *key_buf, /*!< key in dns wire fmt */ + const int key_len) /*!< size of key */ +{ + + DST_KEY *dkey = NULL; + int dnslen; + u_char dns[2048]; + + if (!dst_check_algorithm(alg)) { /*%< make sure alg is available */ + EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg)); + return (NULL); + } + + dkey = dst_s_get_key_struct(key_name, alg, flags, protocol, -1); + + if (dkey == NULL || dkey->dk_func == NULL || + dkey->dk_func->from_dns_key == NULL) + return (dst_free_key(dkey)); + + if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) { + EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n")); + return (dst_free_key(dkey)); + } + + dnslen = dst_key_to_dnskey(dkey, dns, sizeof(dns)); + dkey->dk_id = dst_s_dns_key_id(dns, dnslen); + return (dkey); +} + +int +dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len) +{ + int len; + /* this function will extrac the secret of HMAC into a buffer */ + if (key == NULL) + return (0); + if (key->dk_func != NULL && key->dk_func->to_dns_key != NULL) { + len = key->dk_func->to_dns_key(key, out_buff, buf_len); + if (len < 0) + return (0); + return (len); + } + return (0); +} + +/*% + * dst_s_read_private_key_file + * Function reads in private key from a file. + * Fills out the KEY structure. + * Parameters + * name Name of the key to be read. + * pk_key Structure that the key is returned in. + * in_id Key identifier (tag) + * Return + * 1 if everthing works + * 0 if there is any problem + */ + +static int +dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id, + int in_alg) +{ + int cnt, alg, len, major, minor, file_major, file_minor; + int ret, id; + char filename[PATH_MAX]; + u_char in_buff[RAW_KEY_SIZE], *p; + FILE *fp; + int dnslen; + u_char dns[2048]; + + if (name == NULL || pk_key == NULL) { + EREPORT(("dst_read_private_key_file(): No key name given\n")); + return (0); + } + /* Make the filename */ + if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY, + PATH_MAX) == -1) { + EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n", + name, in_id, PRIVATE_KEY)); + return (0); + } + /* first check if we can find the key file */ + if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) { + EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n", + filename, dst_path[0] ? dst_path : + (char *) getcwd(NULL, PATH_MAX - 1))); + return (0); + } + /* now read the header info from the file */ + if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) { + fclose(fp); + EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n", + filename)); + return (0); + } + /* decrypt key */ + fclose(fp); + if (memcmp(in_buff, "Private-key-format: v", 20) != 0) + goto fail; + len = cnt; + p = in_buff; + + if (!dst_s_verify_str((const char **) (void *)&p, + "Private-key-format: v")) { + EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name)); + goto fail; + } + /* read in file format */ + sscanf((char *)p, "%d.%d", &file_major, &file_minor); + sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor); + if (file_major < 1) { + EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n", + file_major, file_minor, name)); + goto fail; + } else if (file_major > major || file_minor > minor) + EREPORT(( + "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n", + name, file_major, file_minor)); + + while (*p++ != '\n') ; /*%< skip to end of line */ + + if (!dst_s_verify_str((const char **) (void *)&p, "Algorithm: ")) + goto fail; + + if (sscanf((char *)p, "%d", &alg) != 1) + goto fail; + while (*p++ != '\n') ; /*%< skip to end of line */ + + if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name)) + SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name)); + pk_key->dk_key_name = (char *) strdup(name); + + /* allocate and fill in key structure */ + if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL) + goto fail; + + ret = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, &in_buff[len] - p); + if (ret < 0) + goto fail; + + dnslen = dst_key_to_dnskey(pk_key, dns, sizeof(dns)); + id = dst_s_dns_key_id(dns, dnslen); + + /* Make sure the actual key tag matches the input tag used in the filename + */ + if (id != in_id) { + EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id)); + goto fail; + } + pk_key->dk_id = (u_int16_t) id; + pk_key->dk_alg = alg; + memset(in_buff, 0, cnt); + return (1); + + fail: + memset(in_buff, 0, cnt); + return (0); +} + +/*% + * Generate and store a public/private keypair. + * Keys will be stored in formatted files. + * + * Parameters + & + *\par name Name of the new key. Used to create key files + *\li K<name>+<alg>+<id>.public and K<name>+<alg>+<id>.private. + *\par bits Size of the new key in bits. + *\par exp What exponent to use: + *\li 0 use exponent 3 + *\li non-zero use Fermant4 + *\par flags The default value of the DNS Key flags. + *\li The DNS Key RR Flag field is defined in RFC2065, + * section 3.3. The field has 16 bits. + *\par protocol + *\li Default value of the DNS Key protocol field. + *\li The DNS Key protocol field is defined in RFC2065, + * section 3.4. The field has 8 bits. + *\par alg What algorithm to use. Currently defined: + *\li KEY_RSA 1 + *\li KEY_DSA 3 + *\li KEY_HMAC 157 + *\par out_id The key tag is returned. + * + * Return + *\li NULL Failure + *\li non-NULL the generated key pair + * Caller frees the result, and its dk_name pointer. + */ +DST_KEY * +dst_generate_key(const char *name, const int bits, const int exp, + const int flags, const int protocol, const int alg) +{ + DST_KEY *new_key = NULL; + int dnslen; + u_char dns[2048]; + + if (name == NULL) + return (NULL); + + if (!dst_check_algorithm(alg)) { /*%< make sure alg is available */ + EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg)); + return (NULL); + } + + new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits); + if (new_key == NULL) + return (NULL); + if (bits == 0) /*%< null key we are done */ + return (new_key); + if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) { + EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n", + alg)); + return (dst_free_key(new_key)); + } + if (new_key->dk_func->generate(new_key, exp) <= 0) { + EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n", + new_key->dk_key_name, new_key->dk_alg, + new_key->dk_key_size, exp)); + return (dst_free_key(new_key)); + } + + dnslen = dst_key_to_dnskey(new_key, dns, sizeof(dns)); + if (dnslen != UNSUPPORTED_KEYALG) + new_key->dk_id = dst_s_dns_key_id(dns, dnslen); + else + new_key->dk_id = 0; + + return (new_key); +} + +/*% + * Release all data structures pointed to by a key structure. + * + * Parameters + *\li f_key Key structure to be freed. + */ + +DST_KEY * +dst_free_key(DST_KEY *f_key) +{ + + if (f_key == NULL) + return (f_key); + if (f_key->dk_func && f_key->dk_func->destroy) + f_key->dk_KEY_struct = + f_key->dk_func->destroy(f_key->dk_KEY_struct); + else { + EREPORT(("dst_free_key(): Unknown key alg %d\n", + f_key->dk_alg)); + } + if (f_key->dk_KEY_struct) { + free(f_key->dk_KEY_struct); + f_key->dk_KEY_struct = NULL; + } + if (f_key->dk_key_name) + SAFE_FREE(f_key->dk_key_name); + SAFE_FREE(f_key); + return (NULL); +} + +/*% + * Return the maximim size of signature from the key specified in bytes + * + * Parameters + *\li key + * + * Returns + * \li bytes + */ +int +dst_sig_size(DST_KEY *key) { + switch (key->dk_alg) { + case KEY_HMAC_MD5: + return (16); + case KEY_HMAC_SHA1: + return (20); + case KEY_RSA: + return (key->dk_key_size + 7) / 8; + case KEY_DSA: + return (40); + default: + EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg)); + return -1; + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/dst/dst_internal.h b/usr/src/lib/libresolv2_joy/common/dst/dst_internal.h new file mode 100644 index 0000000000..e9bc6fc08d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/dst_internal.h @@ -0,0 +1,155 @@ +#ifndef DST_INTERNAL_H +#define DST_INTERNAL_H + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE. + */ +#include <limits.h> +#include <sys/param.h> +#if (!defined(BSD)) || (BSD < 199306) +# include <sys/bitypes.h> +#else +# include <sys/types.h> +#endif + +#ifndef PATH_MAX +# ifdef POSIX_PATH_MAX +# define PATH_MAX POSIX_PATH_MAX +# else +# define PATH_MAX 255 /*%< this is the value of POSIX_PATH_MAX */ +# endif +#endif + +typedef struct dst_key { + char *dk_key_name; /*%< name of the key */ + int dk_key_size; /*%< this is the size of the key in bits */ + int dk_proto; /*%< what protocols this key can be used for */ + int dk_alg; /*%< algorithm number from key record */ + u_int32_t dk_flags; /*%< and the flags of the public key */ + u_int16_t dk_id; /*%< identifier of the key */ + void *dk_KEY_struct; /*%< pointer to key in crypto pkg fmt */ + struct dst_func *dk_func; /*%< point to cryptto pgk specific function table */ +} DST_KEY; +#define HAS_DST_KEY + +#include <isc/dst.h> +/* + * define what crypto systems are supported for RSA, + * BSAFE is prefered over RSAREF; only one can be set at any time + */ +#if defined(BSAFE) && defined(RSAREF) +# error "Cannot have both BSAFE and RSAREF defined" +#endif + +/* Declare dst_lib specific constants */ +#define KEY_FILE_FORMAT "1.2" + +/* suffixes for key file names */ +#define PRIVATE_KEY "private" +#define PUBLIC_KEY "key" + +/* error handling */ +#ifdef REPORT_ERRORS +#define EREPORT(str) printf str +#else +#define EREPORT(str) (void)0 +#endif + +/* use our own special macro to FRRE memory */ + +#ifndef SAFE_FREE +#define SAFE_FREE(a) \ +do{if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;}} while (0) +#define SAFE_FREE2(a,s) if (a != NULL && (long)s > 0){memset(a,0, s);free(a); a=NULL;} +#endif + +typedef struct dst_func { + int (*sign)(const int mode, DST_KEY *key, void **context, + const u_int8_t *data, const int len, + u_int8_t *signature, const int sig_len); + int (*verify)(const int mode, DST_KEY *key, void **context, + const u_int8_t *data, const int len, + const u_int8_t *signature, const int sig_len); + int (*compare)(const DST_KEY *key1, const DST_KEY *key2); + int (*generate)(DST_KEY *key, int parms); + void *(*destroy)(void *key); + /* conversion functions */ + int (*to_dns_key)(const DST_KEY *key, u_int8_t *out, + const int out_len); + int (*from_dns_key)(DST_KEY *key, const u_int8_t *str, + const int str_len); + int (*to_file_fmt)(const DST_KEY *key, char *out, + const int out_len); + int (*from_file_fmt)(DST_KEY *key, const char *out, + const int out_len); + +} dst_func; + +extern dst_func *dst_t_func[DST_MAX_ALGS]; +extern const char *key_file_fmt_str; +extern const char *dst_path; + +#ifndef DST_HASH_SIZE +#define DST_HASH_SIZE 20 /*%< RIPEMD160 and SHA-1 are 20 bytes MD5 is 16 */ +#endif + +int dst_bsafe_init(void); + +int dst_rsaref_init(void); + +int dst_hmac_md5_init(void); + +int dst_cylink_init(void); + +int dst_eay_dss_init(void); + +/* from higher level support routines */ +int dst_s_calculate_bits( const u_int8_t *str, const int max_bits); +int dst_s_verify_str( const char **buf, const char *str); + + +/* conversion between dns names and key file names */ +size_t dst_s_filename_length( const char *name, const char *suffix); +int dst_s_build_filename( char *filename, const char *name, + u_int16_t id, int alg, const char *suffix, + size_t filename_length); + +FILE *dst_s_fopen (const char *filename, const char *mode, int perm); + +/*% + * read and write network byte order into u_int?_t + * all of these should be retired + */ +u_int16_t dst_s_get_int16( const u_int8_t *buf); +void dst_s_put_int16( u_int8_t *buf, const u_int16_t val); + +u_int32_t dst_s_get_int32( const u_int8_t *buf); +void dst_s_put_int32( u_int8_t *buf, const u_int32_t val); + +#ifdef DUMP +# undef DUMP +# define DUMP(a,b,c,d) dst_s_dump(a,b,c,d) +#else +# define DUMP(a,b,c,d) +#endif +void +dst_s_dump(const int mode, const u_char *data, const int size, + const char *msg); + + + +#endif /* DST_INTERNAL_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c b/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c new file mode 100644 index 0000000000..f005784e7b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c @@ -0,0 +1,488 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifdef HMAC_MD5 +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE. + */ + +/*% + * This file contains an implementation of the HMAC-MD5 algorithm. + */ +#include "port_before.h" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <memory.h> +#include <sys/param.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include "dst_internal.h" + +#ifdef USE_MD5 +# ifndef HAVE_MD5 +# include "md5.h" +# else +# ifdef SOLARIS2 +# include <sys/md5.h> +# endif +# endif +# ifndef _MD5_H_ +# define _MD5_H_ 1 /*%< make sure we do not include rsaref md5.h file */ +# endif +#endif + +#include "port_after.h" + + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + + +typedef struct hmackey { + u_char hk_ipad[64], hk_opad[64]; +} HMAC_Key; + + +/************************************************************************** + * dst_hmac_md5_sign + * Call HMAC signing functions to sign a block of data. + * There are three steps to signing, INIT (initialize structures), + * UPDATE (hash (more) data), FINAL (generate a signature). This + * routine performs one or more of these steps. + * Parameters + * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. + * priv_key key to use for signing. + * context the context to be used in this digest + * data data to be signed. + * len length in bytes of data. + * signature location to store signature. + * sig_len size of the signature location + * returns + * N Success on SIG_MODE_FINAL = returns signature length in bytes + * 0 Success on SIG_MODE_INIT and UPDATE + * <0 Failure + */ + +static int +dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context, + const u_char *data, const int len, + u_char *signature, const int sig_len) +{ + HMAC_Key *key; + int sign_len = 0; + MD5_CTX *ctx = NULL; + + if (d_key == NULL || d_key->dk_KEY_struct == NULL) + return (-1); + + if (mode & SIG_MODE_INIT) + ctx = (MD5_CTX *) malloc(sizeof(*ctx)); + else if (context) + ctx = (MD5_CTX *) *context; + if (ctx == NULL) + return (-1); + + key = (HMAC_Key *) d_key->dk_KEY_struct; + + if (mode & SIG_MODE_INIT) { + MD5Init(ctx); + MD5Update(ctx, key->hk_ipad, HMAC_LEN); + } + + if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) + MD5Update(ctx, data, len); + + if (mode & SIG_MODE_FINAL) { + if (signature == NULL || sig_len < MD5_LEN) + return (SIGN_FINAL_FAILURE); + MD5Final(signature, ctx); + + /* perform outer MD5 */ + MD5Init(ctx); + MD5Update(ctx, key->hk_opad, HMAC_LEN); + MD5Update(ctx, signature, MD5_LEN); + MD5Final(signature, ctx); + sign_len = MD5_LEN; + SAFE_FREE(ctx); + } + else { + if (context == NULL) + return (-1); + *context = (void *) ctx; + } + return (sign_len); +} + + +/************************************************************************** + * dst_hmac_md5_verify() + * Calls HMAC verification routines. There are three steps to + * verification, INIT (initialize structures), UPDATE (hash (more) data), + * FINAL (generate a signature). This routine performs one or more of + * these steps. + * Parameters + * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. + * dkey key to use for verify. + * data data signed. + * len length in bytes of data. + * signature signature. + * sig_len length in bytes of signature. + * returns + * 0 Success + * <0 Failure + */ + +static int +dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context, + const u_char *data, const int len, + const u_char *signature, const int sig_len) +{ + HMAC_Key *key; + MD5_CTX *ctx = NULL; + + if (d_key == NULL || d_key->dk_KEY_struct == NULL) + return (-1); + + if (mode & SIG_MODE_INIT) + ctx = (MD5_CTX *) malloc(sizeof(*ctx)); + else if (context) + ctx = (MD5_CTX *) *context; + if (ctx == NULL) + return (-1); + + key = (HMAC_Key *) d_key->dk_KEY_struct; + if (mode & SIG_MODE_INIT) { + MD5Init(ctx); + MD5Update(ctx, key->hk_ipad, HMAC_LEN); + } + if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) + MD5Update(ctx, data, len); + + if (mode & SIG_MODE_FINAL) { + u_char digest[MD5_LEN]; + if (signature == NULL || key == NULL || sig_len != MD5_LEN) + return (VERIFY_FINAL_FAILURE); + MD5Final(digest, ctx); + + /* perform outer MD5 */ + MD5Init(ctx); + MD5Update(ctx, key->hk_opad, HMAC_LEN); + MD5Update(ctx, digest, MD5_LEN); + MD5Final(digest, ctx); + + SAFE_FREE(ctx); + if (memcmp(digest, signature, MD5_LEN) != 0) + return (VERIFY_FINAL_FAILURE); + } + else { + if (context == NULL) + return (-1); + *context = (void *) ctx; + } + return (0); +} + + +/************************************************************************** + * dst_buffer_to_hmac_md5 + * Converts key from raw data to an HMAC Key + * This function gets in a pointer to the data + * Parameters + * hkey the HMAC key to be filled in + * key the key in raw format + * keylen the length of the key + * Return + * 0 Success + * <0 Failure + */ +static int +dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const int keylen) +{ + int i; + HMAC_Key *hkey = NULL; + MD5_CTX ctx; + int local_keylen = keylen; + u_char tk[MD5_LEN]; + + if (dkey == NULL || key == NULL || keylen < 0) + return (-1); + + if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL) + return (-2); + + memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad)); + memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad)); + + /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */ + if (keylen > HMAC_LEN) { + MD5Init(&ctx); + MD5Update(&ctx, key, keylen); + MD5Final(tk, &ctx); + memset((void *) &ctx, 0, sizeof(ctx)); + key = tk; + local_keylen = MD5_LEN; + } + /* start out by storing key in pads */ + memcpy(hkey->hk_ipad, key, local_keylen); + memcpy(hkey->hk_opad, key, local_keylen); + + /* XOR key with hk_ipad and opad values */ + for (i = 0; i < HMAC_LEN; i++) { + hkey->hk_ipad[i] ^= HMAC_IPAD; + hkey->hk_opad[i] ^= HMAC_OPAD; + } + dkey->dk_key_size = local_keylen; + dkey->dk_KEY_struct = (void *) hkey; + return (1); +} + + +/************************************************************************** + * dst_hmac_md5_key_to_file_format + * Encodes an HMAC Key into the portable file format. + * Parameters + * hkey HMAC KEY structure + * buff output buffer + * buff_len size of output buffer + * Return + * 0 Failure - null input hkey + * -1 Failure - not enough space in output area + * N Success - Length of data returned in buff + */ + +static int +dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff, + const int buff_len) +{ + char *bp; + int len, i, key_len; + u_char key[HMAC_LEN]; + HMAC_Key *hkey; + + if (dkey == NULL || dkey->dk_KEY_struct == NULL) + return (0); + /* + * Using snprintf() would be so much simpler here. + */ + if (buff == NULL || + buff_len <= (int)(strlen(key_file_fmt_str) + + strlen(KEY_FILE_FORMAT) + 4)) + return (-1); /*%< no OR not enough space in output area */ + hkey = (HMAC_Key *) dkey->dk_KEY_struct; + memset(buff, 0, buff_len); /*%< just in case */ + /* write file header */ + sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC"); + + bp = buff + strlen(buff); + + memset(key, 0, HMAC_LEN); + for (i = 0; i < HMAC_LEN; i++) + key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; + for (i = HMAC_LEN - 1; i >= 0; i--) + if (key[i] != 0) + break; + key_len = i + 1; + + if (buff_len - (bp - buff) < 6) + return (-1); + strcat(bp, "Key: "); + bp += strlen("Key: "); + + len = b64_ntop(key, key_len, bp, buff_len - (bp - buff)); + if (len < 0) + return (-1); + bp += len; + if (buff_len - (bp - buff) < 2) + return (-1); + *(bp++) = '\n'; + *bp = '\0'; + + return (bp - buff); +} + + +/************************************************************************** + * dst_hmac_md5_key_from_file_format + * Converts contents of a key file into an HMAC key. + * Parameters + * hkey structure to put key into + * buff buffer containing the encoded key + * buff_len the length of the buffer + * Return + * n >= 0 Foot print of the key converted + * n < 0 Error in conversion + */ + +static int +dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff, + const int buff_len) +{ + const char *p = buff, *eol; + u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode + * it should probably be fixed rather than doing + * this + */ + u_char *tmp; + int key_len, len; + + if (dkey == NULL) + return (-2); + if (buff == NULL || buff_len < 0) + return (-1); + + memset(key, 0, sizeof(key)); + + if (!dst_s_verify_str(&p, "Key: ")) + return (-3); + + eol = strchr(p, '\n'); + if (eol == NULL) + return (-4); + len = eol - p; + tmp = malloc(len + 2); + if (tmp == NULL) + return (-5); + memcpy(tmp, p, len); + *(tmp + len) = 0x0; + key_len = b64_pton((char *)tmp, key, HMAC_LEN+1); /*%< see above */ + SAFE_FREE2(tmp, len + 2); + + if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) { + return (-6); + } + return (0); +} + +/*% + * dst_hmac_md5_to_dns_key() + * function to extract hmac key from DST_KEY structure + * intput: + * in_key: HMAC-MD5 key + * output: + * out_str: buffer to write ot + * out_len: size of output buffer + * returns: + * number of bytes written to output buffer + */ +static int +dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str, + const int out_len) +{ + + HMAC_Key *hkey; + int i; + + if (in_key == NULL || in_key->dk_KEY_struct == NULL || + out_len <= in_key->dk_key_size || out_str == NULL) + return (-1); + + hkey = (HMAC_Key *) in_key->dk_KEY_struct; + for (i = 0; i < in_key->dk_key_size; i++) + out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; + return (i); +} + +/************************************************************************** + * dst_hmac_md5_compare_keys + * Compare two keys for equality. + * Return + * 0 The keys are equal + * NON-ZERO The keys are not equal + */ + +static int +dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2) +{ + HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct; + HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct; + return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN); +} + +/************************************************************************** + * dst_hmac_md5_free_key_structure + * Frees all (none) dynamically allocated structures in hkey + */ + +static void * +dst_hmac_md5_free_key_structure(void *key) +{ + HMAC_Key *hkey = key; + SAFE_FREE(hkey); + return (NULL); +} + + +/*************************************************************************** + * dst_hmac_md5_generate_key + * Creates a HMAC key of size size with a maximum size of 63 bytes + * generating a HMAC key larger than 63 bytes makes no sense as that key + * is digested before use. + */ + +static int +dst_hmac_md5_generate_key(DST_KEY *key, const int nothing) +{ + (void)key; + (void)nothing; + return (-1); +} + +/*% + * dst_hmac_md5_init() Function to answer set up function pointers for HMAC + * related functions + */ +int +dst_hmac_md5_init() +{ + if (dst_t_func[KEY_HMAC_MD5] != NULL) + return (1); + dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func)); + if (dst_t_func[KEY_HMAC_MD5] == NULL) + return (0); + memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func)); + dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign; + dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify; + dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys; + dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key; + dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure; + dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key; + dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5; + dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format; + dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format; + return (1); +} + +#else +#define dst_hmac_md5_init __dst_hmac_md5_init + +int +dst_hmac_md5_init(){ + return (0); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/dst/support.c b/usr/src/lib/libresolv2_joy/common/dst/support.c new file mode 100644 index 0000000000..cc975284b6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/support.c @@ -0,0 +1,339 @@ +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE. + */ + +#include "port_before.h" + +#include <stdio.h> +#include <unistd.h> +#include <memory.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include "dst_internal.h" + +#include "port_after.h" + +/*% + * dst_s_verify_str() + * Validate that the input string(*str) is at the head of the input + * buffer(**buf). If so, move the buffer head pointer (*buf) to + * the first byte of data following the string(*str). + * Parameters + * buf Input buffer. + * str Input string. + * Return + * 0 *str is not the head of **buff + * 1 *str is the head of **buff, *buf is is advanced to + * the tail of **buf. + */ + +int +dst_s_verify_str(const char **buf, const char *str) +{ + int b, s; + if (*buf == NULL) /*%< error checks */ + return (0); + if (str == NULL || *str == '\0') + return (1); + + b = strlen(*buf); /*%< get length of strings */ + s = strlen(str); + if (s > b || strncmp(*buf, str, s)) /*%< check if same */ + return (0); /*%< not a match */ + (*buf) += s; /*%< advance pointer */ + return (1); +} + +/*% + * dst_s_calculate_bits + * Given a binary number represented in a u_char[], determine + * the number of significant bits used. + * Parameters + * str An input character string containing a binary number. + * max_bits The maximum possible significant bits. + * Return + * N The number of significant bits in str. + */ + +int +dst_s_calculate_bits(const u_char *str, const int max_bits) +{ + const u_char *p = str; + u_char i, j = 0x80; + int bits; + for (bits = max_bits; *p == 0x00 && bits > 0; p++) + bits -= 8; + for (i = *p; (i & j) != j; j >>= 1) + bits--; + return (bits); +} + +/*% + * calculates a checksum used in dst for an id. + * takes an array of bytes and a length. + * returns a 16 bit checksum. + */ +u_int16_t +dst_s_id_calc(const u_char *key, const int keysize) +{ + u_int32_t ac; + const u_char *kp = key; + int size = keysize; + + if (!key || (keysize <= 0)) + return (0xffffU); + + for (ac = 0; size > 1; size -= 2, kp += 2) + ac += ((*kp) << 8) + *(kp + 1); + + if (size > 0) + ac += ((*kp) << 8); + ac += (ac >> 16) & 0xffff; + + return (ac & 0xffff); +} + +/*% + * dst_s_dns_key_id() Function to calculate DNSSEC footprint from KEY record + * rdata + * Input: + * dns_key_rdata: the raw data in wire format + * rdata_len: the size of the input data + * Output: + * the key footprint/id calculated from the key data + */ +u_int16_t +dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len) +{ + if (!dns_key_rdata) + return 0; + + /* compute id */ + if (dns_key_rdata[3] == KEY_RSA) /*%< Algorithm RSA */ + return dst_s_get_int16((const u_char *) + &dns_key_rdata[rdata_len - 3]); + else if (dns_key_rdata[3] == KEY_HMAC_MD5) + /* compatibility */ + return 0; + else + /* compute a checksum on the key part of the key rr */ + return dst_s_id_calc(dns_key_rdata, rdata_len); +} + +/*% + * dst_s_get_int16 + * This routine extracts a 16 bit integer from a two byte character + * string. The character string is assumed to be in network byte + * order and may be unaligned. The number returned is in host order. + * Parameter + * buf A two byte character string. + * Return + * The converted integer value. + */ + +u_int16_t +dst_s_get_int16(const u_char *buf) +{ + register u_int16_t a = 0; + a = ((u_int16_t)(buf[0] << 8)) | ((u_int16_t)(buf[1])); + return (a); +} + +/*% + * dst_s_get_int32 + * This routine extracts a 32 bit integer from a four byte character + * string. The character string is assumed to be in network byte + * order and may be unaligned. The number returned is in host order. + * Parameter + * buf A four byte character string. + * Return + * The converted integer value. + */ + +u_int32_t +dst_s_get_int32(const u_char *buf) +{ + register u_int32_t a = 0; + a = ((u_int32_t)(buf[0] << 24)) | ((u_int32_t)(buf[1] << 16)) | + ((u_int32_t)(buf[2] << 8)) | ((u_int32_t)(buf[3])); + return (a); +} + +/*% + * dst_s_put_int16 + * Take a 16 bit integer and store the value in a two byte + * character string. The integer is assumed to be in network + * order and the string is returned in host order. + * + * Parameters + * buf Storage for a two byte character string. + * val 16 bit integer. + */ + +void +dst_s_put_int16(u_int8_t *buf, const u_int16_t val) +{ + buf[0] = (u_int8_t)(val >> 8); + buf[1] = (u_int8_t)(val); +} + +/*% + * dst_s_put_int32 + * Take a 32 bit integer and store the value in a four byte + * character string. The integer is assumed to be in network + * order and the string is returned in host order. + * + * Parameters + * buf Storage for a four byte character string. + * val 32 bit integer. + */ + +void +dst_s_put_int32(u_int8_t *buf, const u_int32_t val) +{ + buf[0] = (u_int8_t)(val >> 24); + buf[1] = (u_int8_t)(val >> 16); + buf[2] = (u_int8_t)(val >> 8); + buf[3] = (u_int8_t)(val); +} + +/*% + * dst_s_filename_length + * + * This function returns the number of bytes needed to hold the + * filename for a key file. '/', '\' and ':' are not allowed. + * form: K<keyname>+<alg>+<id>.<suffix> + * + * Returns 0 if the filename would contain either '\', '/' or ':' + */ +size_t +dst_s_filename_length(const char *name, const char *suffix) +{ + if (name == NULL) + return (0); + if (strrchr(name, '\\')) + return (0); + if (strrchr(name, '/')) + return (0); + if (strrchr(name, ':')) + return (0); + if (suffix == NULL) + return (0); + if (strrchr(suffix, '\\')) + return (0); + if (strrchr(suffix, '/')) + return (0); + if (strrchr(suffix, ':')) + return (0); + return (1 + strlen(name) + 6 + strlen(suffix)); +} + +/*% + * dst_s_build_filename () + * Builds a key filename from the key name, it's id, and a + * suffix. '\', '/' and ':' are not allowed. fA filename is of the + * form: K<keyname><id>.<suffix> + * form: K<keyname>+<alg>+<id>.<suffix> + * + * Returns -1 if the conversion fails: + * if the filename would be too long for space allotted + * if the filename would contain a '\', '/' or ':' + * Returns 0 on success + */ + +int +dst_s_build_filename(char *filename, const char *name, u_int16_t id, + int alg, const char *suffix, size_t filename_length) +{ + u_int32_t my_id; + if (filename == NULL) + return (-1); + memset(filename, 0, filename_length); + if (name == NULL) + return (-1); + if (suffix == NULL) + return (-1); + if (filename_length < 1 + strlen(name) + 4 + 6 + 1 + strlen(suffix)) + return (-1); + my_id = id; + sprintf(filename, "K%s+%03d+%05d.%s", name, alg, my_id, + (const char *) suffix); + if (strrchr(filename, '/')) + return (-1); + if (strrchr(filename, '\\')) + return (-1); + if (strrchr(filename, ':')) + return (-1); + return (0); +} + +/*% + * dst_s_fopen () + * Open a file in the dst_path directory. If perm is specified, the + * file is checked for existence first, and not opened if it exists. + * Parameters + * filename File to open + * mode Mode to open the file (passed directly to fopen) + * perm File permission, if creating a new file. + * Returns + * NULL Failure + * NON-NULL (FILE *) of opened file. + */ +FILE * +dst_s_fopen(const char *filename, const char *mode, int perm) +{ + FILE *fp; + char pathname[PATH_MAX]; + + if (strlen(filename) + strlen(dst_path) >= sizeof(pathname)) + return (NULL); + + if (*dst_path != '\0') { + strcpy(pathname, dst_path); + strcat(pathname, filename); + } else + strcpy(pathname, filename); + + fp = fopen(pathname, mode); + if (perm) + chmod(pathname, perm); + return (fp); +} + +void +dst_s_dump(const int mode, const u_char *data, const int size, + const char *msg) +{ + UNUSED(data); + + if (size > 0) { +#ifdef LONG_TEST + static u_char scratch[1000]; + int n ; + n = b64_ntop(data, scratch, size, sizeof(scratch)); + printf("%s: %x %d %s\n", msg, mode, n, scratch); +#else + printf("%s,%x %d\n", msg, mode, size); +#endif + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c new file mode 100644 index 0000000000..bf960a8acc --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: inet_cidr_ntop.c,v 1.7 2006/10/11 02:18:18 marka Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +static char * +inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size); +static char * +inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size); + +/*% + * char * + * inet_cidr_ntop(af, src, bits, dst, size) + * convert network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by inet_net_ntop() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + */ +char * +inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) { + switch (af) { + case AF_INET: + return (inet_cidr_ntop_ipv4(src, bits, dst, size)); + case AF_INET6: + return (inet_cidr_ntop_ipv6(src, bits, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } +} + +static int +decoct(const u_char *src, int bytes, char *dst, size_t size) { + char *odst = dst; + char *t; + int b; + + for (b = 1; b <= bytes; b++) { + if (size < sizeof "255.") + return (0); + t = dst; + dst += SPRINTF((dst, "%u", *src++)); + if (b != bytes) { + *dst++ = '.'; + *dst = '\0'; + } + size -= (size_t)(dst - t); + } + return (dst - odst); +} + +/*% + * static char * + * inet_cidr_ntop_ipv4(src, bits, dst, size) + * convert IPv4 network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), October 1998 + */ +static char * +inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) { + char *odst = dst; + size_t len = 4; + size_t b; + size_t bytes; + + if ((bits < -1) || (bits > 32)) { + errno = EINVAL; + return (NULL); + } + + /* Find number of significant bytes in address. */ + if (bits == -1) + len = 4; + else + for (len = 1, b = 1 ; b < 4U; b++) + if (*(src + b)) + len = b + 1; + + /* Format whole octets plus nonzero trailing octets. */ + bytes = (((bits <= 0) ? 1 : bits) + 7) / 8; + if (len > bytes) + bytes = len; + b = decoct(src, bytes, dst, size); + if (b == 0U) + goto emsgsize; + dst += b; + size -= b; + + if (bits != -1) { + /* Format CIDR /width. */ + if (size < sizeof "/32") + goto emsgsize; + dst += SPRINTF((dst, "/%u", bits)); + } + + return (odst); + + emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +static char * +inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; + char *tp; + struct { int base, len; } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + if ((bits < -1) || (bits > 128)) { + errno = EINVAL; + return (NULL); + } + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + int n; + + if (src[15] || bits == -1 || bits > 120) + n = 4; + else if (src[14] || bits > 112) + n = 3; + else + n = 2; + n = decoct(src+12, n, tp, sizeof tmp - (tp - tmp)); + if (n == 0) { + errno = EMSGSIZE; + return (NULL); + } + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%x", words[i])); + } + + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp = '\0'; + + if (bits != -1) + tp += SPRINTF((tp, "/%u", bits)); + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = EMSGSIZE; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c new file mode 100644 index 0000000000..07652af463 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: inet_cidr_pton.c,v 1.6 2005/04/27 04:56:19 sra Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <isc/assertions.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +static int inet_cidr_pton_ipv4 __P((const char *src, u_char *dst, + int *bits, int ipv6)); +static int inet_cidr_pton_ipv6 __P((const char *src, u_char *dst, + int *bits)); + +static int getbits(const char *, int ipv6); + +/*% + * int + * inet_cidr_pton(af, src, dst, *bits) + * convert network address from presentation to network format. + * accepts inet_pton()'s input for this "af" plus trailing "/CIDR". + * "dst" is assumed large enough for its "af". "bits" is set to the + * /CIDR prefix length, which can have defaults (like /32 for IPv4). + * return: + * -1 if an error occurred (inspect errno; ENOENT means bad format). + * 0 if successful conversion occurred. + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by inet_net_pton() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + */ +int +inet_cidr_pton(int af, const char *src, void *dst, int *bits) { + switch (af) { + case AF_INET: + return (inet_cidr_pton_ipv4(src, dst, bits, 0)); + case AF_INET6: + return (inet_cidr_pton_ipv6(src, dst, bits)); + default: + errno = EAFNOSUPPORT; + return (-1); + } +} + +static const char digits[] = "0123456789"; + +static int +inet_cidr_pton_ipv4(const char *src, u_char *dst, int *pbits, int ipv6) { + const u_char *odst = dst; + int n, ch, tmp, bits; + size_t size = 4; + + /* Get the mantissa. */ + while (ch = *src++, (isascii(ch) && isdigit(ch))) { + tmp = 0; + do { + n = strchr(digits, ch) - digits; + INSIST(n >= 0 && n <= 9); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); + if (size-- == 0U) + goto emsgsize; + *dst++ = (u_char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + } + + /* Get the prefix length if any. */ + bits = -1; + if (ch == '/' && dst > odst) { + bits = getbits(src, ipv6); + if (bits == -2) + goto enoent; + } else if (ch != '\0') + goto enoent; + + /* Prefix length can default to /32 only if all four octets spec'd. */ + if (bits == -1) { + if (dst - odst == 4) + bits = ipv6 ? 128 : 32; + else + goto enoent; + } + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + + /* If prefix length overspecifies mantissa, life is bad. */ + if (((bits - (ipv6 ? 96 : 0)) / 8) > (dst - odst)) + goto enoent; + + /* Extend address to four octets. */ + while (size-- > 0U) + *dst++ = 0; + + *pbits = bits; + return (0); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + +static int +inet_cidr_pton_ipv6(const char *src, u_char *dst, int *pbits) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + u_int val; + int bits; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + bits = -1; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } else if (*src == '\0') { + return (0); + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_cidr_pton_ipv4(curtok, tp, &bits, 1) == 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /*%< '\\0' was seen by inet_pton4(). */ + } + if (ch == '/') { + bits = getbits(src, 1); + if (bits == -2) + goto enoent; + break; + } + goto enoent; + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + goto emsgsize; + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + goto enoent; + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + + memcpy(dst, tmp, NS_IN6ADDRSZ); + + *pbits = bits; + return (0); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + +static int +getbits(const char *src, int ipv6) { + int bits = 0; + char *cp, ch; + + if (*src == '\0') /*%< syntax */ + return (-2); + do { + ch = *src++; + cp = strchr(digits, ch); + if (cp == NULL) /*%< syntax */ + return (-2); + bits *= 10; + bits += cp - digits; + if (bits == 0 && *src != '\0') /*%< no leading zeros */ + return (-2); + if (bits > (ipv6 ? 128 : 32)) /*%< range error */ + return (-2); + } while (*src != '\0'); + + return (bits); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_data.c b/usr/src/lib/libresolv2_joy/common/inet/inet_data.c new file mode 100644 index 0000000000..58b8c4342d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_data.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$Id: inet_data.c,v 1.4 2005/04/27 04:56:19 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "port_after.h" + +const struct in6_addr isc_in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr isc_in6addr_loopback = IN6ADDR_LOOPBACK_INIT; + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c b/usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c new file mode 100644 index 0000000000..70ac409512 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c @@ -0,0 +1,65 @@ +/* + * 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 const char sccsid[] = "@(#)inet_lnaof.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include "port_before.h" + +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "port_after.h" + +/*% + * Return the local network address portion of an + * internet address; handles class a/b/c network + * number formats. + */ +u_long +inet_lnaof(in) + struct in_addr in; +{ + register u_long i = ntohl(in.s_addr); + + if (IN_CLASSA(i)) + return ((i)&IN_CLASSA_HOST); + else if (IN_CLASSB(i)) + return ((i)&IN_CLASSB_HOST); + else + return ((i)&IN_CLASSC_HOST); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c b/usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c new file mode 100644 index 0000000000..c56cb3eaeb --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c @@ -0,0 +1,68 @@ +/* + * 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 const char sccsid[] = "@(#)inet_makeaddr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include "port_before.h" + +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "port_after.h" + +/*% + * Formulate an Internet address from network + host. Used in + * building addresses stored in the ifnet structure. + */ +struct in_addr +inet_makeaddr(net, host) + u_long net, host; +{ + struct in_addr a; + + if (net < 128U) + a.s_addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST); + else if (net < 65536U) + a.s_addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST); + else if (net < 16777216L) + a.s_addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST); + else + a.s_addr = net | host; + a.s_addr = htonl(a.s_addr); + return (a); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c b/usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c new file mode 100644 index 0000000000..fb28e3cbe5 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.5 2006/06/20 02:50:14 marka Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +static char * inet_net_ntop_ipv4 __P((const u_char *src, int bits, + char *dst, size_t size)); +static char * inet_net_ntop_ipv6 __P((const u_char *src, int bits, + char *dst, size_t size)); + +/*% + * char * + * inet_net_ntop(af, src, bits, dst, size) + * convert network number from network to presentation format. + * generates CIDR style result always. + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * author: + * Paul Vixie (ISC), July 1996 + */ +char * +inet_net_ntop(af, src, bits, dst, size) + int af; + const void *src; + int bits; + char *dst; + size_t size; +{ + switch (af) { + case AF_INET: + return (inet_net_ntop_ipv4(src, bits, dst, size)); + case AF_INET6: + return (inet_net_ntop_ipv6(src, bits, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } +} + +/*% + * static char * + * inet_net_ntop_ipv4(src, bits, dst, size) + * convert IPv4 network number from network to presentation format. + * generates CIDR style result always. + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), July 1996 + */ +static char * +inet_net_ntop_ipv4(src, bits, dst, size) + const u_char *src; + int bits; + char *dst; + size_t size; +{ + char *odst = dst; + char *t; + u_int m; + int b; + + if (bits < 0 || bits > 32) { + errno = EINVAL; + return (NULL); + } + + if (bits == 0) { + if (size < sizeof "0") + goto emsgsize; + *dst++ = '0'; + size--; + *dst = '\0'; + } + + /* Format whole octets. */ + for (b = bits / 8; b > 0; b--) { + if (size <= sizeof "255.") + goto emsgsize; + t = dst; + dst += SPRINTF((dst, "%u", *src++)); + if (b > 1) { + *dst++ = '.'; + *dst = '\0'; + } + size -= (size_t)(dst - t); + } + + /* Format partial octet. */ + b = bits % 8; + if (b > 0) { + if (size <= sizeof ".255") + goto emsgsize; + t = dst; + if (dst != odst) + *dst++ = '.'; + m = ((1 << b) - 1) << (8 - b); + dst += SPRINTF((dst, "%u", *src & m)); + size -= (size_t)(dst - t); + } + + /* Format CIDR /width. */ + if (size <= sizeof "/32") + goto emsgsize; + dst += SPRINTF((dst, "/%u", bits)); + return (odst); + + emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +/*% + * static char * + * inet_net_ntop_ipv6(src, bits, fakebits, dst, size) + * convert IPv6 network number from network to presentation format. + * generates CIDR style result always. Picks the shortest representation + * unless the IP is really IPv4. + * always prints specified number of bits (bits). + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Vadim Kogan (UCB), June 2001 + * Original version (IPv4) by Paul Vixie (ISC), July 1996 + */ + +static char * +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { + u_int m; + int b; + int p; + int zero_s, zero_l, tmp_zero_s, tmp_zero_l; + int i; + int is_ipv4 = 0; + unsigned char inbuf[16]; + char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; + char *cp; + int words; + u_char *s; + + if (bits < 0 || bits > 128) { + errno = EINVAL; + return (NULL); + } + + cp = outbuf; + + if (bits == 0) { + *cp++ = ':'; + *cp++ = ':'; + *cp = '\0'; + } else { + /* Copy src to private buffer. Zero host part. */ + p = (bits + 7) / 8; + memcpy(inbuf, src, p); + memset(inbuf + p, 0, 16 - p); + b = bits % 8; + if (b != 0) { + m = ~0 << (8 - b); + inbuf[p-1] &= m; + } + + s = inbuf; + + /* how many words need to be displayed in output */ + words = (bits + 15) / 16; + if (words == 1) + words = 2; + + /* Find the longest substring of zero's */ + zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; + for (i = 0; i < (words * 2); i += 2) { + if ((s[i] | s[i+1]) == 0) { + if (tmp_zero_l == 0) + tmp_zero_s = i / 2; + tmp_zero_l++; + } else { + if (tmp_zero_l && zero_l < tmp_zero_l) { + zero_s = tmp_zero_s; + zero_l = tmp_zero_l; + tmp_zero_l = 0; + } + } + } + + if (tmp_zero_l && zero_l < tmp_zero_l) { + zero_s = tmp_zero_s; + zero_l = tmp_zero_l; + } + + if (zero_l != words && zero_s == 0 && ((zero_l == 6) || + ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) || + ((zero_l == 7 && s[14] != 0 && s[15] != 1))))) + is_ipv4 = 1; + + /* Format whole words. */ + for (p = 0; p < words; p++) { + if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) { + /* Time to skip some zeros */ + if (p == zero_s) + *cp++ = ':'; + if (p == words - 1) + *cp++ = ':'; + s++; + s++; + continue; + } + + if (is_ipv4 && p > 5 ) { + *cp++ = (p == 6) ? ':' : '.'; + cp += SPRINTF((cp, "%u", *s++)); + /* we can potentially drop the last octet */ + if (p != 7 || bits > 120) { + *cp++ = '.'; + cp += SPRINTF((cp, "%u", *s++)); + } + } else { + if (cp != outbuf) + *cp++ = ':'; + cp += SPRINTF((cp, "%x", *s * 256 + s[1])); + s += 2; + } + } + } + /* Format CIDR /width. */ + sprintf(cp, "/%u", bits); + if (strlen(outbuf) + 1 > size) + goto emsgsize; + strcpy(dst, outbuf); + + return (dst); + +emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c b/usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c new file mode 100644 index 0000000000..8d8bfb1f64 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1996, 1998, 1999, 2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: inet_net_pton.c,v 1.10 2008/11/14 02:36:51 marka Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <isc/assertions.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/*% + * static int + * inet_net_pton_ipv4(src, dst, size) + * convert IPv4 network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not an IPv4 network specification. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 + */ +static int +inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { + static const char xdigits[] = "0123456789abcdef"; + static const char digits[] = "0123456789"; + int n, ch, tmp = 0, dirty, bits; + const u_char *odst = dst; + + ch = *src++; + if (ch == '0' && (src[0] == 'x' || src[0] == 'X') + && isascii((unsigned char)(src[1])) + && isxdigit((unsigned char)(src[1]))) { + /* Hexadecimal: Eat nybble string. */ + if (size <= 0U) + goto emsgsize; + dirty = 0; + src++; /*%< skip x or X. */ + while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) { + if (isupper(ch)) + ch = tolower(ch); + n = strchr(xdigits, ch) - xdigits; + INSIST(n >= 0 && n <= 15); + if (dirty == 0) + tmp = n; + else + tmp = (tmp << 4) | n; + if (++dirty == 2) { + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) tmp; + dirty = 0; + } + } + if (dirty) { /*%< Odd trailing nybble? */ + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) (tmp << 4); + } + } else if (isascii(ch) && isdigit(ch)) { + /* Decimal: eat dotted digit string. */ + for (;;) { + tmp = 0; + do { + n = strchr(digits, ch) - digits; + INSIST(n >= 0 && n <= 9); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = *src++) != '\0' && + isascii(ch) && isdigit(ch)); + if (size-- <= 0U) + goto emsgsize; + *dst++ = (u_char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + ch = *src++; + if (!isascii(ch) || !isdigit(ch)) + goto enoent; + } + } else + goto enoent; + + bits = -1; + if (ch == '/' && isascii((unsigned char)(src[0])) && + isdigit((unsigned char)(src[0])) && dst > odst) { + /* CIDR width specifier. Nothing can follow it. */ + ch = *src++; /*%< Skip over the /. */ + bits = 0; + do { + n = strchr(digits, ch) - digits; + INSIST(n >= 0 && n <= 9); + bits *= 10; + bits += n; + if (bits > 32) + goto enoent; + } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); + if (ch != '\0') + goto enoent; + } + + /* Firey death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + /* If no CIDR spec was given, infer width from net class. */ + if (bits == -1) { + if (*odst >= 240) /*%< Class E */ + bits = 32; + else if (*odst >= 224) /*%< Class D */ + bits = 8; + else if (*odst >= 192) /*%< Class C */ + bits = 24; + else if (*odst >= 128) /*%< Class B */ + bits = 16; + else /*%< Class A */ + bits = 8; + /* If imputed mask is narrower than specified octets, widen. */ + if (bits < ((dst - odst) * 8)) + bits = (dst - odst) * 8; + /* + * If there are no additional bits specified for a class D + * address adjust bits to 4. + */ + if (bits == 8 && *odst == 224) + bits = 4; + } + /* Extend network to cover the actual mask. */ + while (bits > ((dst - odst) * 8)) { + if (size-- <= 0U) + goto emsgsize; + *dst++ = '\0'; + } + return (bits); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + +static int +getbits(const char *src, int *bitsp) { + static const char digits[] = "0123456789"; + int n; + int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) { + if (n++ != 0 && val == 0) /*%< no leading zeros */ + return (0); + val *= 10; + val += (pch - digits); + if (val > 128) /*%< range */ + return (0); + continue; + } + return (0); + } + if (n == 0) + return (0); + *bitsp = val; + return (1); +} + +static int +getv4(const char *src, u_char *dst, int *bitsp) { + static const char digits[] = "0123456789"; + u_char *odst = dst; + int n; + u_int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) { + if (n++ != 0 && val == 0) /*%< no leading zeros */ + return (0); + val *= 10; + val += (pch - digits); + if (val > 255) /*%< range */ + return (0); + continue; + } + if (ch == '.' || ch == '/') { + if (dst - odst > 3) /*%< too many octets? */ + return (0); + *dst++ = val; + if (ch == '/') + return (getbits(src, bitsp)); + val = 0; + n = 0; + continue; + } + return (0); + } + if (n == 0) + return (0); + if (dst - odst > 3) /*%< too many octets? */ + return (0); + *dst++ = val; + return (1); +} + +static int +inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + u_int val; + int digits; + int bits; + size_t bytes; + int words; + int ipv4; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + goto enoent; + curtok = src; + saw_xdigit = 0; + val = 0; + digits = 0; + bits = -1; + ipv4 = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (++digits > 4) + goto enoent; + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + goto enoent; + colonp = tp; + continue; + } else if (*src == '\0') + goto enoent; + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + digits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + getv4(curtok, tp, &bits) > 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + ipv4 = 1; + break; /*%< '\\0' was seen by inet_pton4(). */ + } + if (ch == '/' && getbits(src, &bits) > 0) + break; + goto enoent; + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + goto enoent; + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (bits == -1) + bits = 128; + + words = (bits + 15) / 16; + if (words < 2) + words = 2; + if (ipv4) + words = 8; + endp = tmp + 2 * words; + + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + goto enoent; + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + goto enoent; + + bytes = (bits + 7) / 8; + if (bytes > size) + goto emsgsize; + memcpy(dst, tmp, bytes); + return (bits); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + +/*% + * int + * inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +inet_net_pton(int af, const char *src, void *dst, size_t size) { + switch (af) { + case AF_INET: + return (inet_net_pton_ipv4(src, dst, size)); + case AF_INET6: + return (inet_net_pton_ipv6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (-1); + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_neta.c b/usr/src/lib/libresolv2_joy/common/inet/inet_neta.c new file mode 100644 index 0000000000..63a6c201a4 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_neta.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: inet_neta.c,v 1.3 2005/04/27 04:56:20 sra Exp $"; +#endif + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/*% + * char * + * inet_neta(src, dst, size) + * format a u_long network number into presentation format. + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * format of ``src'' is as for inet_network(). + * author: + * Paul Vixie (ISC), July 1996 + */ +char * +inet_neta(src, dst, size) + u_long src; + char *dst; + size_t size; +{ + char *odst = dst; + char *tp; + + while (src & 0xffffffff) { + u_char b = (src & 0xff000000) >> 24; + + src <<= 8; + if (b) { + if (size < sizeof "255.") + goto emsgsize; + tp = dst; + dst += SPRINTF((dst, "%u", b)); + if (src != 0L) { + *dst++ = '.'; + *dst = '\0'; + } + size -= (size_t)(dst - tp); + } + } + if (dst == odst) { + if (size < sizeof "0.0.0.0") + goto emsgsize; + strcpy(dst, "0.0.0.0"); + } + return (odst); + + emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_netof.c b/usr/src/lib/libresolv2_joy/common/inet/inet_netof.c new file mode 100644 index 0000000000..c228e3d818 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_netof.c @@ -0,0 +1,64 @@ +/* + * 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 const char sccsid[] = "@(#)inet_netof.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include "port_before.h" + +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "port_after.h" + +/*% + * Return the network number from an internet + * address; handles class a/b/c network #'s. + */ +u_long +inet_netof(in) + struct in_addr in; +{ + register u_long i = ntohl(in.s_addr); + + if (IN_CLASSA(i)) + return (((i)&IN_CLASSA_NET) >> IN_CLASSA_NSHIFT); + else if (IN_CLASSB(i)) + return (((i)&IN_CLASSB_NET) >> IN_CLASSB_NSHIFT); + else + return (((i)&IN_CLASSC_NET) >> IN_CLASSC_NSHIFT); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_network.c b/usr/src/lib/libresolv2_joy/common/inet/inet_network.c new file mode 100644 index 0000000000..47976cff68 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/inet_network.c @@ -0,0 +1,106 @@ +/* + * 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 const char sccsid[] = "@(#)inet_network.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> + +#include "port_after.h" + +/*% + * Internet network address interpretation routine. + * The library routines call this routine to interpret + * network numbers. + */ +u_long +inet_network(cp) + register const char *cp; +{ + register u_long val, base, n, i; + register char c; + u_long parts[4], *pp = parts; + int digit; + +again: + val = 0; base = 10; digit = 0; + if (*cp == '0') + digit = 1, base = 8, cp++; + if (*cp == 'x' || *cp == 'X') + base = 16, cp++; + while ((c = *cp) != 0) { + if (isdigit((unsigned char)c)) { + if (base == 8U && (c == '8' || c == '9')) + return (INADDR_NONE); + val = (val * base) + (c - '0'); + cp++; + digit = 1; + continue; + } + if (base == 16U && isxdigit((unsigned char)c)) { + val = (val << 4) + + (c + 10 - (islower((unsigned char)c) ? 'a' : 'A')); + cp++; + digit = 1; + continue; + } + break; + } + if (!digit) + return (INADDR_NONE); + if (pp >= parts + 4 || val > 0xffU) + return (INADDR_NONE); + if (*cp == '.') { + *pp++ = val, cp++; + goto again; + } + if (*cp && !isspace(*cp&0xff)) + return (INADDR_NONE); + *pp++ = val; + n = pp - parts; + if (n > 4U) + return (INADDR_NONE); + for (val = 0, i = 0; i < n; i++) { + val <<= 8; + val |= parts[i] & 0xff; + } + return (val); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c b/usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c new file mode 100644 index 0000000000..a9972e6e32 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: nsap_addr.c,v 1.5 2005/07/28 06:51:48 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <resolv_joy.h> +#include <resolv_mt.h> + +#include "port_after.h" + +static char +xtob(int c) { + return (c - (((c >= '0') && (c <= '9')) ? '0' : '7')); +} + +u_int +inet_nsap_addr(const char *ascii, u_char *binary, int maxlen) { + u_char c, nib; + u_int len = 0; + + if (ascii[0] != '0' || (ascii[1] != 'x' && ascii[1] != 'X')) + return (0); + ascii += 2; + + while ((c = *ascii++) != '\0' && len < (u_int)maxlen) { + if (c == '.' || c == '+' || c == '/') + continue; + if (!isascii(c)) + return (0); + if (islower(c)) + c = toupper(c); + if (isxdigit(c)) { + nib = xtob(c); + c = *ascii++; + if (c != '\0') { + c = toupper(c); + if (isxdigit(c)) { + *binary++ = (nib << 4) | xtob(c); + len++; + } else + return (0); + } + else + return (0); + } + else + return (0); + } + return (len); +} + +char * +inet_nsap_ntoa(int binlen, const u_char *binary, char *ascii) { + int nib; + int i; + char *tmpbuf = inet_nsap_ntoa_tmpbuf; + char *start; + + if (ascii) + start = ascii; + else { + ascii = tmpbuf; + start = tmpbuf; + } + + *ascii++ = '0'; + *ascii++ = 'x'; + + if (binlen > 255) + binlen = 255; + + for (i = 0; i < binlen; i++) { + nib = *binary >> 4; + *ascii++ = nib + (nib < 10 ? '0' : '7'); + nib = *binary++ & 0x0f; + *ascii++ = nib + (nib < 10 ? '0' : '7'); + if (((i % 2) == 0 && (i + 1) < binlen)) + *ascii++ = '.'; + } + *ascii = '\0'; + return (start); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns.c b/usr/src/lib/libresolv2_joy/common/irs/dns.c new file mode 100644 index 0000000000..119308074e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief + * dns.c --- this is the top-level accessor function for the dns + */ + +#include "port_before.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <resolv_joy.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "hesiod.h" +#include "dns_p.h" + +/* forward */ + +static void dns_close(struct irs_acc *); +static struct __res_state * dns_res_get(struct irs_acc *); +static void dns_res_set(struct irs_acc *, struct __res_state *, + void (*)(void *)); + +/* public */ + +struct irs_acc * +irs_dns_acc(const char *options) { + struct irs_acc *acc; + struct dns_p *dns; + + UNUSED(options); + + if (!(acc = memget(sizeof *acc))) { + errno = ENOMEM; + return (NULL); + } + memset(acc, 0x5e, sizeof *acc); + if (!(dns = memget(sizeof *dns))) { + errno = ENOMEM; + memput(acc, sizeof *acc); + return (NULL); + } + memset(dns, 0x5e, sizeof *dns); + dns->res = NULL; + dns->free_res = NULL; + if (hesiod_init(&dns->hes_ctx) < 0) { + /* + * We allow the dns accessor class to initialize + * despite hesiod failing to initialize correctly, + * since dns host queries don't depend on hesiod. + */ + dns->hes_ctx = NULL; + } + acc->private = dns; +#ifdef WANT_IRS_GR + acc->gr_map = irs_dns_gr; +#else + acc->gr_map = NULL; +#endif +#ifdef WANT_IRS_PW + acc->pw_map = irs_dns_pw; +#else + acc->pw_map = NULL; +#endif + acc->sv_map = irs_dns_sv; + acc->pr_map = irs_dns_pr; + acc->ho_map = irs_dns_ho; + acc->nw_map = irs_dns_nw; + acc->ng_map = irs_nul_ng; + acc->res_get = dns_res_get; + acc->res_set = dns_res_set; + acc->close = dns_close; + return (acc); +} + +/* methods */ +static struct __res_state * +dns_res_get(struct irs_acc *this) { + struct dns_p *dns = (struct dns_p *)this->private; + + if (dns->res == NULL) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (res == NULL) + return (NULL); + memset(res, 0, sizeof *res); + dns_res_set(this, res, free); + } + + if ((dns->res->options & RES_INIT) == 0U && + res_ninit(dns->res) < 0) + return (NULL); + + return (dns->res); +} + +static void +dns_res_set(struct irs_acc *this, struct __res_state *res, + void (*free_res)(void *)) { + struct dns_p *dns = (struct dns_p *)this->private; + + if (dns->res && dns->free_res) { + res_nclose(dns->res); + (*dns->free_res)(dns->res); + } + dns->res = res; + dns->free_res = free_res; +} + +static void +dns_close(struct irs_acc *this) { + struct dns_p *dns; + + dns = (struct dns_p *)this->private; + if (dns->res && dns->free_res) + (*dns->free_res)(dns->res); + if (dns->hes_ctx) + hesiod_end(dns->hes_ctx); + memput(dns, sizeof *dns); + memput(this, sizeof *this); +} + diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c b/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c new file mode 100644 index 0000000000..b47e26f21a --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c @@ -0,0 +1,1139 @@ +/* + * Portions Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1996-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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 (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. + */ + +/* from gethostnamadr.c 8.1 (Berkeley) 6/4/93 */ +/* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */ + +/* Imports. */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "dns_p.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) sprintf x +#endif + +/* Definitions. */ + +#define MAXALIASES 35 +#define MAXADDRS 35 + +#define MAXPACKET (65535) /*%< Maximum TCP message size */ +#define BOUNDS_CHECK(ptr, count) \ + if ((ptr) + (count) > eom) { \ + had_error++; \ + continue; \ + } else (void)0 + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +struct dns_res_target { + struct dns_res_target *next; + querybuf qbuf; /*%< query buffer */ + u_char *answer; /*%< buffer to put answer */ + int anslen; /*%< size of answer buffer */ + int qclass, qtype; /*%< class and type of query */ + int action; /*%< condition whether query is really issued */ + char qname[MAXDNAME +1]; /*%< domain name */ +#if 0 + int n; /*%< result length */ +#endif +}; +enum {RESTGT_DOALWAYS, RESTGT_AFTERFAILURE, RESTGT_IGNORE}; +enum {RESQRY_SUCCESS, RESQRY_FAIL}; + +struct pvt { + struct hostent host; + char * h_addr_ptrs[MAXADDRS + 1]; + char * host_aliases[MAXALIASES]; + char hostbuf[8*1024]; + u_char host_addr[16]; /*%< IPv4 or IPv6 */ + struct __res_state *res; + void (*free_res)(void *); +}; + +typedef union { + int32_t al; + char ac; +} align; + +static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; +static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; +/* Note: the IPv6 loopback address is in the "tunnel" space */ +static const u_char v6local[] = { 0,0, 0,1 }; /*%< last 4 bytes of IPv6 addr */ +/* Forwards. */ + +static void ho_close(struct irs_ho *this); +static struct hostent * ho_byname(struct irs_ho *this, const char *name); +static struct hostent * ho_byname2(struct irs_ho *this, const char *name, + int af); +static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, + int len, int af); +static struct hostent * ho_next(struct irs_ho *this); +static void ho_rewind(struct irs_ho *this); +static void ho_minimize(struct irs_ho *this); +static struct __res_state * ho_res_get(struct irs_ho *this); +static void ho_res_set(struct irs_ho *this, + struct __res_state *res, + void (*free_res)(void *)); +static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, + const struct addrinfo *pai); + +static void map_v4v6_hostent(struct hostent *hp, char **bp, + char *ep); +static void addrsort(res_state, char **, int); +static struct hostent * gethostans(struct irs_ho *this, + const u_char *ansbuf, int anslen, + const char *qname, int qtype, + int af, int size, + struct addrinfo **ret_aip, + const struct addrinfo *pai); +static int add_hostent(struct pvt *pvt, char *bp, char **hap, + struct addrinfo *ai); +static int init(struct irs_ho *this); + +/* Exports. */ + +struct irs_ho * +irs_dns_ho(struct irs_acc *this) { + struct irs_ho *ho; + struct pvt *pvt; + + UNUSED(this); + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + + if (!(ho = memget(sizeof *ho))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(ho, 0x5e, sizeof *ho); + ho->private = pvt; + ho->close = ho_close; + ho->byname = ho_byname; + ho->byname2 = ho_byname2; + ho->byaddr = ho_byaddr; + ho->next = ho_next; + ho->rewind = ho_rewind; + ho->minimize = ho_minimize; + ho->res_get = ho_res_get; + ho->res_set = ho_res_set; + ho->addrinfo = ho_addrinfo; + return (ho); +} + +/* Methods. */ + +static void +ho_close(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + ho_minimize(this); + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct hostent * +ho_byname(struct irs_ho *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *hp; + + if (init(this) == -1) + return (NULL); + + if (pvt->res->options & RES_USE_INET6) { + hp = ho_byname2(this, name, AF_INET6); + if (hp) + return (hp); + } + return (ho_byname2(this, name, AF_INET)); +} + +static struct hostent * +ho_byname2(struct irs_ho *this, const char *name, int af) +{ + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *hp = NULL; + int n, size; + char tmp[NS_MAXDNAME]; + const char *cp; + struct addrinfo ai; + struct dns_res_target *q, *p; + int querystate = RESQRY_FAIL; + + if (init(this) == -1) + return (NULL); + + q = memget(sizeof(*q)); + if (q == NULL) { + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = ENOMEM; + goto cleanup; + } + memset(q, 0, sizeof(*q)); + + switch (af) { + case AF_INET: + size = INADDRSZ; + q->qclass = C_IN; + q->qtype = T_A; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->action = RESTGT_DOALWAYS; + break; + case AF_INET6: + size = IN6ADDRSZ; + q->qclass = C_IN; + q->qtype = T_AAAA; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->action = RESTGT_DOALWAYS; + break; + default: + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = EAFNOSUPPORT; + hp = NULL; + goto cleanup; + } + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_nquery() since we are not the only + * function that looks up host names. + */ + if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name, + tmp, sizeof tmp))) + name = cp; + + for (p = q; p; p = p->next) { + switch(p->action) { + case RESTGT_DOALWAYS: + break; + case RESTGT_AFTERFAILURE: + if (querystate == RESQRY_SUCCESS) + continue; + break; + case RESTGT_IGNORE: + continue; + } + + if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype, + p->answer, p->anslen)) < 0) { + querystate = RESQRY_FAIL; + continue; + } + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = af; + if ((hp = gethostans(this, p->answer, n, name, p->qtype, + af, size, NULL, + (const struct addrinfo *)&ai)) != NULL) + goto cleanup; /*%< no more loop is necessary */ + querystate = RESQRY_FAIL; + continue; + } + + cleanup: + if (q != NULL) + memput(q, sizeof(*q)); + return(hp); +} + +static struct hostent * +ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) +{ + struct pvt *pvt = (struct pvt *)this->private; + const u_char *uaddr = addr; + char *qp; + struct hostent *hp = NULL; + struct addrinfo ai; + struct dns_res_target *q, *q2, *p; + int n, size, i; + int querystate = RESQRY_FAIL; + + if (init(this) == -1) + return (NULL); + + q = memget(sizeof(*q)); + q2 = memget(sizeof(*q2)); + if (q == NULL || q2 == NULL) { + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = ENOMEM; + goto cleanup; + } + memset(q, 0, sizeof(*q)); + memset(q2, 0, sizeof(*q2)); + + if (af == AF_INET6 && len == IN6ADDRSZ && + (!memcmp(uaddr, mapped, sizeof mapped) || + (!memcmp(uaddr, tunnelled, sizeof tunnelled) && + memcmp(&uaddr[sizeof tunnelled], v6local, sizeof(v6local))))) { + /* Unmap. */ + addr = (const char *)addr + sizeof mapped; + uaddr += sizeof mapped; + af = AF_INET; + len = INADDRSZ; + } + switch (af) { + case AF_INET: + size = INADDRSZ; + q->qclass = C_IN; + q->qtype = T_PTR; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->action = RESTGT_DOALWAYS; + break; + case AF_INET6: + size = IN6ADDRSZ; + q->qclass = C_IN; + q->qtype = T_PTR; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->next = q2; + q->action = RESTGT_DOALWAYS; + q2->qclass = C_IN; + q2->qtype = T_PTR; + q2->answer = q2->qbuf.buf; + q2->anslen = sizeof(q2->qbuf); + if ((pvt->res->options & RES_NO_NIBBLE2) != 0U) + q2->action = RESTGT_IGNORE; + else + q2->action = RESTGT_AFTERFAILURE; + break; + default: + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + hp = NULL; + goto cleanup; + } + if (size > len) { + errno = EINVAL; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + hp = NULL; + goto cleanup; + } + switch (af) { + case AF_INET: + qp = q->qname; + (void) sprintf(qp, "%u.%u.%u.%u.in-addr.arpa", + (uaddr[3] & 0xff), + (uaddr[2] & 0xff), + (uaddr[1] & 0xff), + (uaddr[0] & 0xff)); + break; + case AF_INET6: + if (q->action != RESTGT_IGNORE) { + const char *nibsuff = res_get_nibblesuffix(pvt->res); + qp = q->qname; + for (n = IN6ADDRSZ - 1; n >= 0; n--) { + i = SPRINTF((qp, "%x.%x.", + uaddr[n] & 0xf, + (uaddr[n] >> 4) & 0xf)); + if (i != 4) + abort(); + qp += i; + } + if (strlen(q->qname) + strlen(nibsuff) + 1 > + sizeof q->qname) { + errno = ENAMETOOLONG; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + hp = NULL; + goto cleanup; + } + strcpy(qp, nibsuff); /* (checked) */ + } + if (q2->action != RESTGT_IGNORE) { + const char *nibsuff2 = res_get_nibblesuffix2(pvt->res); + qp = q2->qname; + for (n = IN6ADDRSZ - 1; n >= 0; n--) { + i = SPRINTF((qp, "%x.%x.", + uaddr[n] & 0xf, + (uaddr[n] >> 4) & 0xf)); + if (i != 4) + abort(); + qp += i; + } + if (strlen(q2->qname) + strlen(nibsuff2) + 1 > + sizeof q2->qname) { + errno = ENAMETOOLONG; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + hp = NULL; + goto cleanup; + } + strcpy(qp, nibsuff2); /* (checked) */ + } + break; + default: + abort(); + } + + for (p = q; p; p = p->next) { + switch(p->action) { + case RESTGT_DOALWAYS: + break; + case RESTGT_AFTERFAILURE: + if (querystate == RESQRY_SUCCESS) + continue; + break; + case RESTGT_IGNORE: + continue; + } + + if ((n = res_nquery(pvt->res, p->qname, p->qclass, p->qtype, + p->answer, p->anslen)) < 0) { + querystate = RESQRY_FAIL; + continue; + } + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = af; + hp = gethostans(this, p->answer, n, p->qname, T_PTR, af, size, + NULL, (const struct addrinfo *)&ai); + if (!hp) { + querystate = RESQRY_FAIL; + continue; + } + + memcpy(pvt->host_addr, addr, len); + pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; + pvt->h_addr_ptrs[1] = NULL; + if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) { + map_v4v6_address((char*)pvt->host_addr, + (char*)pvt->host_addr); + pvt->host.h_addrtype = AF_INET6; + pvt->host.h_length = IN6ADDRSZ; + } + + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + goto cleanup; /*%< no more loop is necessary. */ + } + hp = NULL; /*%< H_ERRNO was set by subroutines */ + cleanup: + if (q != NULL) + memput(q, sizeof(*q)); + if (q2 != NULL) + memput(q2, sizeof(*q2)); + return(hp); +} + +static struct hostent * +ho_next(struct irs_ho *this) { + + UNUSED(this); + + return (NULL); +} + +static void +ho_rewind(struct irs_ho *this) { + + UNUSED(this); + + /* NOOP */ +} + +static void +ho_minimize(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res) + res_nclose(pvt->res); +} + +static struct __res_state * +ho_res_get(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + ho_res_set(this, res, free); + } + + return (pvt->res); +} + +/* XXX */ +extern struct addrinfo *addr2addrinfo __P((const struct addrinfo *, + const char *)); + +static struct addrinfo * +ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) +{ + struct pvt *pvt = (struct pvt *)this->private; + int n; + char tmp[NS_MAXDNAME]; + const char *cp; + struct dns_res_target *q, *q2, *p; + struct addrinfo sentinel, *cur; + int querystate = RESQRY_FAIL; + + if (init(this) == -1) + return (NULL); + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + q = memget(sizeof(*q)); + q2 = memget(sizeof(*q2)); + if (q == NULL || q2 == NULL) { + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = ENOMEM; + goto cleanup; + } + memset(q, 0, sizeof(*q2)); + memset(q2, 0, sizeof(*q2)); + + switch (pai->ai_family) { + case AF_UNSPEC: + /* prefer IPv6 */ + q->qclass = C_IN; + q->qtype = T_AAAA; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->next = q2; + q->action = RESTGT_DOALWAYS; + q2->qclass = C_IN; + q2->qtype = T_A; + q2->answer = q2->qbuf.buf; + q2->anslen = sizeof(q2->qbuf); + q2->action = RESTGT_DOALWAYS; + break; + case AF_INET: + q->qclass = C_IN; + q->qtype = T_A; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->action = RESTGT_DOALWAYS; + break; + case AF_INET6: + q->qclass = C_IN; + q->qtype = T_AAAA; + q->answer = q->qbuf.buf; + q->anslen = sizeof(q->qbuf); + q->action = RESTGT_DOALWAYS; + break; + default: + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /*%< better error? */ + goto cleanup; + } + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_nquery() since we are not the only + * function that looks up host names. + */ + if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name, + tmp, sizeof tmp))) + name = cp; + + for (p = q; p; p = p->next) { + struct addrinfo *ai; + + switch(p->action) { + case RESTGT_DOALWAYS: + break; + case RESTGT_AFTERFAILURE: + if (querystate == RESQRY_SUCCESS) + continue; + break; + case RESTGT_IGNORE: + continue; + } + + if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype, + p->answer, p->anslen)) < 0) { + querystate = RESQRY_FAIL; + continue; + } + (void)gethostans(this, p->answer, n, name, p->qtype, + pai->ai_family, /*%< XXX: meaningless */ + 0, &ai, pai); + if (ai) { + querystate = RESQRY_SUCCESS; + cur->ai_next = ai; + while (cur->ai_next) + cur = cur->ai_next; + } else + querystate = RESQRY_FAIL; + } + + cleanup: + if (q != NULL) + memput(q, sizeof(*q)); + if (q2 != NULL) + memput(q2, sizeof(*q2)); + return(sentinel.ai_next); +} + +static void +ho_res_set(struct irs_ho *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; +} + +/* Private. */ + +static struct hostent * +gethostans(struct irs_ho *this, + const u_char *ansbuf, int anslen, const char *qname, int qtype, + int af, int size, /*!< meaningless for addrinfo cases */ + struct addrinfo **ret_aip, const struct addrinfo *pai) +{ + struct pvt *pvt = (struct pvt *)this->private; + int type, class, ancount, qdcount, n, haveanswer, had_error; + int error = NETDB_SUCCESS; + int (*name_ok)(const char *); + const HEADER *hp; + const u_char *eom; + const u_char *eor; + const u_char *cp; + const char *tname; + const char *hname; + char *bp, *ep, **ap, **hap; + char tbuf[MAXDNAME+1]; + struct addrinfo sentinel, *cur, ai; + + if (pai == NULL) abort(); + if (ret_aip != NULL) + *ret_aip = NULL; + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + tname = qname; + eom = ansbuf + 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; + case T_PTR: + name_ok = res_dnok; + break; + default: + abort(); + } + + pvt->host.h_addrtype = af; + pvt->host.h_length = size; + hname = pvt->host.h_name = NULL; + + /* + * Find first satisfactory answer. + */ + if (ansbuf + HFIXEDSZ > eom) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + hp = (const HEADER *)ansbuf; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = pvt->hostbuf; + ep = pvt->hostbuf + sizeof(pvt->hostbuf); + cp = ansbuf + HFIXEDSZ; + if (qdcount != 1) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + n = dn_expand(ansbuf, eom, cp, bp, ep - bp); + if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + cp += n + QFIXEDSZ; + if (cp > eom) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { + /* res_nsend() 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(pvt->res, NO_RECOVERY); + return (NULL); + } + pvt->host.h_name = bp; + hname = bp; + bp += n; + /* The qname can be abbreviated, but hname is now absolute. */ + qname = pvt->host.h_name; + } + ap = pvt->host_aliases; + *ap = NULL; + pvt->host.h_aliases = pvt->host_aliases; + hap = pvt->h_addr_ptrs; + *hap = NULL; + pvt->host.h_addr_list = pvt->h_addr_ptrs; + haveanswer = 0; + had_error = 0; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(ansbuf, eom, cp, bp, ep - bp); + if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) { + had_error++; + continue; + } + cp += n; /*%< name */ + BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ); + type = ns_get16(cp); + cp += INT16SZ; /*%< type */ + class = ns_get16(cp); + cp += INT16SZ + INT32SZ; /*%< class, TTL */ + n = ns_get16(cp); + cp += INT16SZ; /*%< len */ + BOUNDS_CHECK(cp, n); + if (class != C_IN) { + cp += n; + continue; + } + eor = cp + n; + if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && + type == T_CNAME) { + if (haveanswer) { + int level = LOG_CRIT; +#ifdef LOG_SECURITY + level |= LOG_SECURITY; +#endif + syslog(level, + "gethostans: possible attempt to exploit buffer overflow while looking up %s", + *qname ? qname : "."); + } + n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf); + if (n < 0 || !maybe_ok(pvt->res, tbuf, name_ok)) { + had_error++; + continue; + } + cp += n; + /* Store alias. */ + if (ap >= &pvt->host_aliases[MAXALIASES-1]) + continue; + *ap++ = bp; + n = strlen(bp) + 1; /*%< for the \\0 */ + bp += n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /*%< for the \\0 */ + if (n > (ep - bp) || n > MAXHOSTNAMELEN) { + had_error++; + continue; + } + strcpy(bp, tbuf); /* (checked) */ + pvt->host.h_name = bp; + hname = bp; + bp += n; + continue; + } + if (qtype == T_PTR && type == T_CNAME) { + n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf); + if (n < 0 || !maybe_dnok(pvt->res, tbuf)) { + had_error++; + continue; + } + cp += n; +#ifdef RES_USE_DNAME + if ((pvt->res->options & RES_USE_DNAME) != 0U) +#endif + { + /* + * We may be able to check this regardless + * of the USE_DNAME bit, but we add the check + * for now since the DNAME support is + * experimental. + */ + if (ns_samename(tname, bp) != 1) + continue; + } + /* Get canonical name. */ + n = strlen(tbuf) + 1; /*%< for the \\0 */ + if (n > (ep - bp)) { + had_error++; + continue; + } + strcpy(bp, tbuf); /* (checked) */ + tname = bp; + bp += n; + continue; + } + if (qtype == T_ANY) { + if (!(type == T_A || type == T_AAAA)) { + cp += n; + continue; + } + } else if (type != qtype) { + cp += n; + continue; + } + switch (type) { + case T_PTR: + if (ret_aip != NULL) { + /* addrinfo never needs T_PTR */ + cp += n; + continue; + } + if (ns_samename(tname, bp) != 1) { + cp += n; + continue; + } + n = dn_expand(ansbuf, eor, cp, bp, ep - bp); + if (n < 0 || !maybe_hnok(pvt->res, bp) || + n >= MAXHOSTNAMELEN) { + had_error++; + break; + } + cp += n; + if (!haveanswer) { + pvt->host.h_name = bp; + hname = bp; + } + else if (ap < &pvt->host_aliases[MAXALIASES-1]) + *ap++ = bp; + else + n = -1; + if (n != -1) { + n = strlen(bp) + 1; /*%< for the \\0 */ + bp += n; + } + break; + case T_A: + case T_AAAA: + if (ns_samename(hname, bp) != 1) { + cp += n; + continue; + } + if (type == T_A && n != INADDRSZ) { + cp += n; + continue; + } + if (type == T_AAAA && n != IN6ADDRSZ) { + cp += n; + continue; + } + + /* make addrinfo. don't overwrite constant PAI */ + ai = *pai; + ai.ai_family = (type == T_AAAA) ? AF_INET6 : AF_INET; + cur->ai_next = addr2addrinfo( + (const struct addrinfo *)&ai, + (const char *)cp); + if (cur->ai_next == NULL) + had_error++; + + if (!haveanswer) { + int nn; + + nn = strlen(bp) + 1; /*%< for the \\0 */ + if (nn >= MAXHOSTNAMELEN) { + cp += n; + had_error++; + continue; + } + pvt->host.h_name = bp; + hname = bp; + bp += nn; + } + /* Ensure alignment. */ + bp = (char *)(((u_long)bp + (sizeof(align) - 1)) & + ~(sizeof(align) - 1)); + /* Avoid overflows. */ + if (bp + n > &pvt->hostbuf[sizeof(pvt->hostbuf) - 1]) { + had_error++; + continue; + } + if (ret_aip) { /*%< need addrinfo. keep it. */ + while (cur->ai_next) + cur = cur->ai_next; + } else if (cur->ai_next) { /*%< need hostent */ + struct addrinfo *aip = cur->ai_next; + + for (aip = cur->ai_next; aip; + aip = aip->ai_next) { + int m; + + m = add_hostent(pvt, bp, hap, aip); + if (m < 0) { + had_error++; + break; + } + if (m == 0) + continue; + if (hap < &pvt->h_addr_ptrs[MAXADDRS]) + hap++; + *hap = NULL; + bp += m; + } + + freeaddrinfo(cur->ai_next); + cur->ai_next = NULL; + } + cp += n; + break; + default: + abort(); + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { + if (ret_aip == NULL) { + *ap = NULL; + *hap = NULL; + + if (pvt->res->nsort && hap != pvt->h_addr_ptrs && + qtype == T_A) + addrsort(pvt->res, pvt->h_addr_ptrs, + hap - pvt->h_addr_ptrs); + if (pvt->host.h_name == NULL) { + n = strlen(qname) + 1; /*%< for the \\0 */ + if (n > (ep - bp) || n >= MAXHOSTNAMELEN) + goto no_recovery; + strcpy(bp, qname); /* (checked) */ + pvt->host.h_name = bp; + bp += n; + } + if (pvt->res->options & RES_USE_INET6) + map_v4v6_hostent(&pvt->host, &bp, ep); + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + return (&pvt->host); + } else { + if ((pai->ai_flags & AI_CANONNAME) != 0) { + if (pvt->host.h_name == NULL) { + sentinel.ai_next->ai_canonname = + strdup(qname); + } + else { + sentinel.ai_next->ai_canonname = + strdup(pvt->host.h_name); + } + } + *ret_aip = sentinel.ai_next; + return(NULL); + } + } + no_recovery: + if (sentinel.ai_next) { + /* this should be impossible, but check it for safety */ + freeaddrinfo(sentinel.ai_next); + } + if (error == NETDB_SUCCESS) + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + else + RES_SET_H_ERRNO(pvt->res, error); + return(NULL); +} + +static int +add_hostent(struct pvt *pvt, char *bp, char **hap, struct addrinfo *ai) +{ + int addrlen; + char *addrp; + const char **tap; + char *obp = bp; + + switch(ai->ai_addr->sa_family) { + case AF_INET6: + addrlen = IN6ADDRSZ; + addrp = (char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; + break; + case AF_INET: + addrlen = INADDRSZ; + addrp = (char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr; + break; + default: + return(-1); /*%< abort? */ + } + + /* Ensure alignment. */ + bp = (char *)(((u_long)bp + (sizeof(align) - 1)) & + ~(sizeof(align) - 1)); + /* Avoid overflows. */ + if (bp + addrlen > &pvt->hostbuf[sizeof(pvt->hostbuf) - 1]) + return(-1); + if (hap >= &pvt->h_addr_ptrs[MAXADDRS]) + return(0); /*%< fail, but not treat it as an error. */ + /* Suppress duplicates. */ + for (tap = (const char **)pvt->h_addr_ptrs; + *tap != NULL; + tap++) + if (memcmp(*tap, addrp, addrlen) == 0) + break; + if (*tap != NULL) + return (0); + + memcpy(*hap = bp, addrp, addrlen); + return((bp + addrlen) - obp); +} + +static void +map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) { + char **ap; + + if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) + return; + hp->h_addrtype = AF_INET6; + hp->h_length = IN6ADDRSZ; + for (ap = hp->h_addr_list; *ap; ap++) { + int i = (u_long)*bpp % sizeof(align); + + if (i != 0) + i = sizeof(align) - i; + + if ((ep - *bpp) < (i + IN6ADDRSZ)) { + /* Out of memory. Truncate address list here. */ + *ap = NULL; + return; + } + *bpp += i; + map_v4v6_address(*ap, *bpp); + *ap = *bpp; + *bpp += IN6ADDRSZ; + } +} + +static void +addrsort(res_state statp, char **ap, int num) { + int i, j, needsort = 0, aval[MAXADDRS]; + char **p; + + p = ap; + for (i = 0; i < num; i++, p++) { + for (j = 0 ; (unsigned)j < statp->nsort; j++) + if (statp->sort_list[j].addr.s_addr == + (((struct in_addr *)(*p))->s_addr & + statp->sort_list[j].mask)) + break; + aval[i] = j; + if (needsort == 0 && i > 0 && j < aval[i-1]) + needsort = i; + } + if (!needsort) + return; + + while (needsort < num) { + for (j = needsort - 1; j >= 0; j--) { + if (aval[j] > aval[j+1]) { + char *hp; + + i = aval[j]; + aval[j] = aval[j+1]; + aval[j+1] = i; + + hp = ap[j]; + ap[j] = ap[j+1]; + ap[j+1] = hp; + + } else + break; + } + needsort++; + } +} + +static int +init(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res && !ho_res_get(this)) + return (-1); + if (((pvt->res->options & RES_INIT) == 0U) && + res_ninit(pvt->res) == -1) + return (-1); + return (0); +} diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c b/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c new file mode 100644 index 0000000000..9d9dbf9cd2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports. */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "dns_p.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) sprintf x +#endif + +/* Definitions. */ + +#define MAXALIASES 35 + +#define MAXPACKET (64*1024) + +struct pvt { + struct nwent net; + char * ali[MAXALIASES]; + char buf[BUFSIZ+1]; + struct __res_state * res; + void (*free_res)(void *); +}; + +typedef union { + long al; + char ac; +} align; + +enum by_what { by_addr, by_name }; + +/* Forwards. */ + +static void nw_close(struct irs_nw *); +static struct nwent * nw_byname(struct irs_nw *, const char *, int); +static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int); +static struct nwent * nw_next(struct irs_nw *); +static void nw_rewind(struct irs_nw *); +static void nw_minimize(struct irs_nw *); +static struct __res_state * nw_res_get(struct irs_nw *this); +static void nw_res_set(struct irs_nw *this, + struct __res_state *res, + void (*free_res)(void *)); + +static struct nwent * get1101byaddr(struct irs_nw *, u_char *, int); +static struct nwent * get1101byname(struct irs_nw *, const char *); +static struct nwent * get1101answer(struct irs_nw *, + u_char *ansbuf, int anslen, + enum by_what by_what, + int af, const char *name, + const u_char *addr, int addrlen); +static struct nwent * get1101mask(struct irs_nw *this, struct nwent *); +static int make1101inaddr(const u_char *, int, char *, int); +static void normalize_name(char *name); +static int init(struct irs_nw *this); + +/* Exports. */ + +struct irs_nw * +irs_dns_nw(struct irs_acc *this) { + struct irs_nw *nw; + struct pvt *pvt; + + UNUSED(this); + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + if (!(nw = memget(sizeof *nw))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(nw, 0x5e, sizeof *nw); + nw->private = pvt; + nw->close = nw_close; + nw->byname = nw_byname; + nw->byaddr = nw_byaddr; + nw->next = nw_next; + nw->rewind = nw_rewind; + nw->minimize = nw_minimize; + nw->res_get = nw_res_get; + nw->res_set = nw_res_set; + return (nw); +} + +/* Methods. */ + +static void +nw_close(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + nw_minimize(this); + + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct nwent * +nw_byname(struct irs_nw *this, const char *name, int af) { + struct pvt *pvt = (struct pvt *)this->private; + + if (init(this) == -1) + return (NULL); + + switch (af) { + case AF_INET: + return (get1101byname(this, name)); + default: + (void)NULL; + } + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = EAFNOSUPPORT; + return (NULL); +} + +static struct nwent * +nw_byaddr(struct irs_nw *this, void *net, int len, int af) { + struct pvt *pvt = (struct pvt *)this->private; + + if (init(this) == -1) + return (NULL); + + switch (af) { + case AF_INET: + return (get1101byaddr(this, net, len)); + default: + (void)NULL; + } + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = EAFNOSUPPORT; + return (NULL); +} + +static struct nwent * +nw_next(struct irs_nw *this) { + + UNUSED(this); + + return (NULL); +} + +static void +nw_rewind(struct irs_nw *this) { + UNUSED(this); + /* NOOP */ +} + +static void +nw_minimize(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res) + res_nclose(pvt->res); +} + +static struct __res_state * +nw_res_get(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + nw_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +nw_res_set(struct irs_nw *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; +} + +/* Private. */ + +static struct nwent * +get1101byname(struct irs_nw *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + u_char *ansbuf; + int anslen; + struct nwent *result; + + ansbuf = memget(MAXPACKET); + if (ansbuf == NULL) { + errno = ENOMEM; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + anslen = res_nsearch(pvt->res, name, C_IN, T_PTR, ansbuf, MAXPACKET); + if (anslen < 0) { + memput(ansbuf, MAXPACKET); + return (NULL); + } + result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_name, + AF_INET, name, NULL, 0)); + memput(ansbuf, MAXPACKET); + return (result); +} + +static struct nwent * +get1101byaddr(struct irs_nw *this, u_char *net, int len) { + struct pvt *pvt = (struct pvt *)this->private; + char qbuf[sizeof "255.255.255.255.in-addr.arpa"]; + struct nwent *result; + u_char *ansbuf; + int anslen; + + if (len < 1 || len > 32) { + errno = EINVAL; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + if (make1101inaddr(net, len, qbuf, sizeof qbuf) < 0) + return (NULL); + ansbuf = memget(MAXPACKET); + if (ansbuf == NULL) { + errno = ENOMEM; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + anslen = res_nquery(pvt->res, qbuf, C_IN, T_PTR, ansbuf, MAXPACKET); + if (anslen < 0) { + memput(ansbuf, MAXPACKET); + return (NULL); + } + result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_addr, + AF_INET, NULL, net, len)); + memput(ansbuf, MAXPACKET); + return (result); +} + +static struct nwent * +get1101answer(struct irs_nw *this, + u_char *ansbuf, int anslen, enum by_what by_what, + int af, const char *name, const u_char *addr, int addrlen) +{ + struct pvt *pvt = (struct pvt *)this->private; + int type, class, ancount, qdcount, haveanswer; + char *bp, *ep, **ap; + u_char *cp, *eom; + HEADER *hp; + + /* Initialize, and parse header. */ + eom = ansbuf + anslen; + if (ansbuf + HFIXEDSZ > eom) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + hp = (HEADER *)ansbuf; + cp = ansbuf + HFIXEDSZ; + qdcount = ntohs(hp->qdcount); + while (qdcount-- > 0) { + int n = dn_skipname(cp, eom); + cp += n + QFIXEDSZ; + if (n < 0 || cp > eom) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + } + ancount = ntohs(hp->ancount); + if (!ancount) { + if (hp->aa) + RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); + else + RES_SET_H_ERRNO(pvt->res, TRY_AGAIN); + return (NULL); + } + + /* Prepare a return structure. */ + bp = pvt->buf; + ep = pvt->buf + sizeof(pvt->buf); + pvt->net.n_name = NULL; + pvt->net.n_aliases = pvt->ali; + pvt->net.n_addrtype = af; + pvt->net.n_addr = NULL; + pvt->net.n_length = addrlen; + + /* Save input key if given. */ + switch (by_what) { + case by_name: + if (name != NULL) { + int n = strlen(name) + 1; + + if (n > (ep - bp)) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + pvt->net.n_name = strcpy(bp, name); /* (checked) */ + bp += n; + } + break; + case by_addr: + if (addr != NULL && addrlen != 0) { + int n = addrlen / 8 + ((addrlen % 8) != 0); + + if (INADDRSZ > (ep - bp)) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + memset(bp, 0, INADDRSZ); + memcpy(bp, addr, n); + pvt->net.n_addr = bp; + bp += INADDRSZ; + } + break; + default: + abort(); + } + + /* Parse the answer, collect aliases. */ + ap = pvt->ali; + haveanswer = 0; + while (--ancount >= 0 && cp < eom) { + int n = dn_expand(ansbuf, eom, cp, bp, ep - bp); + + cp += n; /*%< Owner */ + if (n < 0 || !maybe_dnok(pvt->res, bp) || + cp + 3 * INT16SZ + INT32SZ > eom) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + GETSHORT(type, cp); /*%< Type */ + GETSHORT(class, cp); /*%< Class */ + cp += INT32SZ; /*%< TTL */ + GETSHORT(n, cp); /*%< RDLENGTH */ + if (class == C_IN && type == T_PTR) { + int nn; + + nn = dn_expand(ansbuf, eom, cp, bp, ep - bp); + if (nn < 0 || !maybe_hnok(pvt->res, bp) || nn != n) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + normalize_name(bp); + switch (by_what) { + case by_addr: { + if (pvt->net.n_name == NULL) + pvt->net.n_name = bp; + else if (ns_samename(pvt->net.n_name, bp) == 1) + break; + else + *ap++ = bp; + nn = strlen(bp) + 1; + bp += nn; + haveanswer++; + break; + } + case by_name: { + u_int b1, b2, b3, b4; + + if (pvt->net.n_addr != NULL || + sscanf(bp, "%u.%u.%u.%u.in-addr.arpa", + &b1, &b2, &b3, &b4) != 4) + break; + if ((ep - bp) < INADDRSZ) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + return (NULL); + } + pvt->net.n_addr = bp; + *bp++ = b4; + *bp++ = b3; + *bp++ = b2; + *bp++ = b1; + pvt->net.n_length = INADDRSZ * 8; + haveanswer++; + } + } + } + cp += n; /*%< RDATA */ + } + if (!haveanswer) { + RES_SET_H_ERRNO(pvt->res, TRY_AGAIN); + return (NULL); + } + *ap = NULL; + + return (&pvt->net); +} + +static struct nwent * +get1101mask(struct irs_nw *this, struct nwent *nwent) { + struct pvt *pvt = (struct pvt *)this->private; + char qbuf[sizeof "255.255.255.255.in-addr.arpa"], owner[MAXDNAME]; + int anslen, type, class, ancount, qdcount; + u_char *ansbuf, *cp, *eom; + HEADER *hp; + + if (!nwent) + return (NULL); + if (make1101inaddr(nwent->n_addr, nwent->n_length, qbuf, sizeof qbuf) + < 0) { + /* "First, do no harm." */ + return (nwent); + } + + ansbuf = memget(MAXPACKET); + if (ansbuf == NULL) { + errno = ENOMEM; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + /* Query for the A RR that would hold this network's mask. */ + anslen = res_nquery(pvt->res, qbuf, C_IN, T_A, ansbuf, MAXPACKET); + if (anslen < HFIXEDSZ) { + memput(ansbuf, MAXPACKET); + return (nwent); + } + + /* Initialize, and parse header. */ + hp = (HEADER *)ansbuf; + cp = ansbuf + HFIXEDSZ; + eom = ansbuf + anslen; + qdcount = ntohs(hp->qdcount); + while (qdcount-- > 0) { + int n = dn_skipname(cp, eom); + cp += n + QFIXEDSZ; + if (n < 0 || cp > eom) { + memput(ansbuf, MAXPACKET); + return (nwent); + } + } + ancount = ntohs(hp->ancount); + + /* Parse the answer, collect aliases. */ + while (--ancount >= 0 && cp < eom) { + int n = dn_expand(ansbuf, eom, cp, owner, sizeof owner); + + if (n < 0 || !maybe_dnok(pvt->res, owner)) + break; + cp += n; /*%< Owner */ + if (cp + 3 * INT16SZ + INT32SZ > eom) + break; + GETSHORT(type, cp); /*%< Type */ + GETSHORT(class, cp); /*%< Class */ + cp += INT32SZ; /*%< TTL */ + GETSHORT(n, cp); /*%< RDLENGTH */ + if (cp + n > eom) + break; + if (n == INADDRSZ && class == C_IN && type == T_A && + ns_samename(qbuf, owner) == 1) { + /* This A RR indicates the actual netmask. */ + int nn, mm; + + nwent->n_length = 0; + for (nn = 0; nn < INADDRSZ; nn++) + for (mm = 7; mm >= 0; mm--) + if (cp[nn] & (1 << mm)) + nwent->n_length++; + else + break; + } + cp += n; /*%< RDATA */ + } + memput(ansbuf, MAXPACKET); + return (nwent); +} + +static int +make1101inaddr(const u_char *net, int bits, char *name, int size) { + int n, m; + char *ep; + + ep = name + size; + + /* Zero fill any whole bytes left out of the prefix. */ + for (n = (32 - bits) / 8; n > 0; n--) { + if (ep - name < (int)(sizeof "0.")) + goto emsgsize; + m = SPRINTF((name, "0.")); + name += m; + } + + /* Format the partial byte, if any, within the prefix. */ + if ((n = bits % 8) != 0) { + if (ep - name < (int)(sizeof "255.")) + goto emsgsize; + m = SPRINTF((name, "%u.", + net[bits / 8] & ~((1 << (8 - n)) - 1))); + name += m; + } + + /* Format the whole bytes within the prefix. */ + for (n = bits / 8; n > 0; n--) { + if (ep - name < (int)(sizeof "255.")) + goto emsgsize; + m = SPRINTF((name, "%u.", net[n - 1])); + name += m; + } + + /* Add the static text. */ + if (ep - name < (int)(sizeof "in-addr.arpa")) + goto emsgsize; + (void) SPRINTF((name, "in-addr.arpa")); + return (0); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + +static void +normalize_name(char *name) { + char *t; + + /* Make lower case. */ + for (t = name; *t; t++) + if (isascii((unsigned char)*t) && isupper((unsigned char)*t)) + *t = tolower((*t)&0xff); + + /* Remove trailing dots. */ + while (t > name && t[-1] == '.') + *--t = '\0'; +} + +static int +init(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res && !nw_res_get(this)) + return (-1); + if (((pvt->res->options & RES_INIT) == 0U) && + res_ninit(pvt->res) == -1) + return (-1); + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_p.h b/usr/src/lib/libresolv2_joy/common/irs/dns_p.h new file mode 100644 index 0000000000..d85ae2a238 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_p.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: dns_p.h,v 1.4 2005/04/27 04:56:22 sra Exp $ + */ + +#ifndef _DNS_P_H_INCLUDED +#define _DNS_P_H_INCLUDED + +#define maybe_ok(res, nm, ok) (((res)->options & RES_NOCHECKNAME) != 0U || \ + (ok)(nm) != 0) +#define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok) +#define maybe_dnok(res, dn) maybe_ok((res), (dn), res_dnok) + +/*% + * Object state. + */ +struct dns_p { + void *hes_ctx; + struct __res_state *res; + void (*free_res) __P((void *)); +}; + +/* + * Methods. + */ + +extern struct irs_gr * irs_dns_gr __P((struct irs_acc *)); +extern struct irs_pw * irs_dns_pw __P((struct irs_acc *)); +extern struct irs_sv * irs_dns_sv __P((struct irs_acc *)); +extern struct irs_pr * irs_dns_pr __P((struct irs_acc *)); +extern struct irs_ho * irs_dns_ho __P((struct irs_acc *)); +extern struct irs_nw * irs_dns_nw __P((struct irs_acc *)); + +#endif /*_DNS_P_H_INCLUDED*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c b/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c new file mode 100644 index 0000000000..0fb533e656 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "hesiod.h" +#include "dns_p.h" + +/* Types. */ + +struct pvt { + struct dns_p * dns; + struct protoent proto; + char * prbuf; +}; + +/* Forward. */ + +static void pr_close(struct irs_pr *); +static struct protoent * pr_byname(struct irs_pr *, const char *); +static struct protoent * pr_bynumber(struct irs_pr *, int); +static struct protoent * pr_next(struct irs_pr *); +static void pr_rewind(struct irs_pr *); +static void pr_minimize(struct irs_pr *); +static struct __res_state * pr_res_get(struct irs_pr *); +static void pr_res_set(struct irs_pr *, + struct __res_state *, + void (*)(void *)); + +static struct protoent * parse_hes_list(struct irs_pr *, char **); + +/* Public. */ + +struct irs_pr * +irs_dns_pr(struct irs_acc *this) { + struct dns_p *dns = (struct dns_p *)this->private; + struct pvt *pvt; + struct irs_pr *pr; + + if (!dns->hes_ctx) { + errno = ENODEV; + return (NULL); + } + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + if (!(pr = memget(sizeof *pr))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(pr, 0x5e, sizeof *pr); + pvt->dns = dns; + pr->private = pvt; + pr->byname = pr_byname; + pr->bynumber = pr_bynumber; + pr->next = pr_next; + pr->rewind = pr_rewind; + pr->close = pr_close; + pr->minimize = pr_minimize; + pr->res_get = pr_res_get; + pr->res_set = pr_res_set; + return (pr); +} + +/* Methods. */ + +static void +pr_close(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->proto.p_aliases) + free(pvt->proto.p_aliases); + if (pvt->prbuf) + free(pvt->prbuf); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct protoent * +pr_byname(struct irs_pr *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + struct protoent *proto; + char **hes_list; + + if (!(hes_list = hesiod_resolve(dns->hes_ctx, name, "protocol"))) + return (NULL); + + proto = parse_hes_list(this, hes_list); + hesiod_free_list(dns->hes_ctx, hes_list); + return (proto); +} + +static struct protoent * +pr_bynumber(struct irs_pr *this, int num) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + struct protoent *proto; + char numstr[16]; + char **hes_list; + + sprintf(numstr, "%d", num); + if (!(hes_list = hesiod_resolve(dns->hes_ctx, numstr, "protonum"))) + return (NULL); + + proto = parse_hes_list(this, hes_list); + hesiod_free_list(dns->hes_ctx, hes_list); + return (proto); +} + +static struct protoent * +pr_next(struct irs_pr *this) { + UNUSED(this); + errno = ENODEV; + return (NULL); +} + +static void +pr_rewind(struct irs_pr *this) { + UNUSED(this); + /* NOOP */ +} + +static void +pr_minimize(struct irs_pr *this) { + UNUSED(this); + /* NOOP */ +} + +static struct __res_state * +pr_res_get(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + + return (__hesiod_res_get(dns->hes_ctx)); +} + +static void +pr_res_set(struct irs_pr *this, struct __res_state * res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + + __hesiod_res_set(dns->hes_ctx, res, free_res); +} + +/* Private. */ + +static struct protoent * +parse_hes_list(struct irs_pr *this, char **hes_list) { + struct pvt *pvt = (struct pvt *)this->private; + char *p, *cp, **cpp, **new; + int num = 0; + int max = 0; + + for (cpp = hes_list; *cpp; cpp++) { + cp = *cpp; + + /* Strip away comments, if any. */ + if ((p = strchr(cp, '#'))) + *p = 0; + + /* Skip blank lines. */ + p = cp; + while (*p && !isspace((unsigned char)*p)) + p++; + if (!*p) + continue; + + /* OK, we've got a live one. Let's parse it for real. */ + if (pvt->prbuf) + free(pvt->prbuf); + pvt->prbuf = strdup(cp); + + p = pvt->prbuf; + pvt->proto.p_name = p; + while (*p && !isspace((unsigned char)*p)) + p++; + if (!*p) + continue; + *p++ = '\0'; + + pvt->proto.p_proto = atoi(p); + while (*p && !isspace((unsigned char)*p)) + p++; + if (*p) + *p++ = '\0'; + + while (*p) { + if ((num + 1) >= max || !pvt->proto.p_aliases) { + max += 10; + new = realloc(pvt->proto.p_aliases, + max * sizeof(char *)); + if (!new) { + errno = ENOMEM; + goto cleanup; + } + pvt->proto.p_aliases = new; + } + pvt->proto.p_aliases[num++] = p; + while (*p && !isspace((unsigned char)*p)) + p++; + if (*p) + *p++ = '\0'; + } + if (!pvt->proto.p_aliases) + pvt->proto.p_aliases = malloc(sizeof(char *)); + if (!pvt->proto.p_aliases) + goto cleanup; + pvt->proto.p_aliases[num] = NULL; + return (&pvt->proto); + } + + cleanup: + if (pvt->proto.p_aliases) { + free(pvt->proto.p_aliases); + pvt->proto.p_aliases = NULL; + } + if (pvt->prbuf) { + free(pvt->prbuf); + pvt->prbuf = NULL; + } + return (NULL); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c b/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c new file mode 100644 index 0000000000..720c230595 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> + +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "hesiod.h" +#include "dns_p.h" + +/* Definitions */ + +struct pvt { + struct dns_p * dns; + struct servent serv; + char * svbuf; + struct __res_state * res; + void (*free_res)(void *); +}; + +/* Forward. */ + +static void sv_close(struct irs_sv *); +static struct servent * sv_byname(struct irs_sv *, + const char *, const char *); +static struct servent * sv_byport(struct irs_sv *, int, const char *); +static struct servent * sv_next(struct irs_sv *); +static void sv_rewind(struct irs_sv *); +static void sv_minimize(struct irs_sv *); +#ifdef SV_RES_SETGET +static struct __res_state * sv_res_get(struct irs_sv *); +static void sv_res_set(struct irs_sv *, + struct __res_state *, + void (*)(void *)); +#endif + +static struct servent * parse_hes_list(struct irs_sv *, + char **, const char *); + +/* Public */ + +struct irs_sv * +irs_dns_sv(struct irs_acc *this) { + struct dns_p *dns = (struct dns_p *)this->private; + struct irs_sv *sv; + struct pvt *pvt; + + if (!dns || !dns->hes_ctx) { + errno = ENODEV; + return (NULL); + } + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->dns = dns; + if (!(sv = memget(sizeof *sv))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(sv, 0x5e, sizeof *sv); + sv->private = pvt; + sv->byname = sv_byname; + sv->byport = sv_byport; + sv->next = sv_next; + sv->rewind = sv_rewind; + sv->close = sv_close; + sv->minimize = sv_minimize; +#ifdef SV_RES_SETGET + sv->res_get = sv_res_get; + sv->res_set = sv_res_set; +#else + sv->res_get = NULL; /*%< sv_res_get; */ + sv->res_set = NULL; /*%< sv_res_set; */ +#endif + return (sv); +} + +/* Methods */ + +static void +sv_close(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->serv.s_aliases) + free(pvt->serv.s_aliases); + if (pvt->svbuf) + free(pvt->svbuf); + + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct servent * +sv_byname(struct irs_sv *this, const char *name, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + struct servent *s; + char **hes_list; + + if (!(hes_list = hesiod_resolve(dns->hes_ctx, name, "service"))) + return (NULL); + + s = parse_hes_list(this, hes_list, proto); + hesiod_free_list(dns->hes_ctx, hes_list); + return (s); +} + +static struct servent * +sv_byport(struct irs_sv *this, int port, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + struct servent *s; + char portstr[16]; + char **hes_list; + + sprintf(portstr, "%d", ntohs(port)); + if (!(hes_list = hesiod_resolve(dns->hes_ctx, portstr, "port"))) + return (NULL); + + s = parse_hes_list(this, hes_list, proto); + hesiod_free_list(dns->hes_ctx, hes_list); + return (s); +} + +static struct servent * +sv_next(struct irs_sv *this) { + UNUSED(this); + errno = ENODEV; + return (NULL); +} + +static void +sv_rewind(struct irs_sv *this) { + UNUSED(this); + /* NOOP */ +} + +/* Private */ + +static struct servent * +parse_hes_list(struct irs_sv *this, char **hes_list, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + char *p, *cp, **cpp, **new; + int proto_len; + int num = 0; + int max = 0; + + for (cpp = hes_list; *cpp; cpp++) { + cp = *cpp; + + /* Strip away comments, if any. */ + if ((p = strchr(cp, '#'))) + *p = 0; + + /* Check to make sure the protocol matches. */ + p = cp; + while (*p && !isspace((unsigned char)*p)) + p++; + if (!*p) + continue; + if (proto) { + proto_len = strlen(proto); + if (strncasecmp(++p, proto, proto_len) != 0) + continue; + if (p[proto_len] && !isspace(p[proto_len]&0xff)) + continue; + } + /* OK, we've got a live one. Let's parse it for real. */ + if (pvt->svbuf) + free(pvt->svbuf); + pvt->svbuf = strdup(cp); + + p = pvt->svbuf; + pvt->serv.s_name = p; + while (*p && !isspace(*p&0xff)) + p++; + if (!*p) + continue; + *p++ = '\0'; + + pvt->serv.s_proto = p; + while (*p && !isspace(*p&0xff)) + p++; + if (!*p) + continue; + *p++ = '\0'; + + pvt->serv.s_port = htons((u_short) atoi(p)); + while (*p && !isspace(*p&0xff)) + p++; + if (*p) + *p++ = '\0'; + + while (*p) { + if ((num + 1) >= max || !pvt->serv.s_aliases) { + max += 10; + new = realloc(pvt->serv.s_aliases, + max * sizeof(char *)); + if (!new) { + errno = ENOMEM; + goto cleanup; + } + pvt->serv.s_aliases = new; + } + pvt->serv.s_aliases[num++] = p; + while (*p && !isspace(*p&0xff)) + p++; + if (*p) + *p++ = '\0'; + } + if (!pvt->serv.s_aliases) + pvt->serv.s_aliases = malloc(sizeof(char *)); + if (!pvt->serv.s_aliases) + goto cleanup; + pvt->serv.s_aliases[num] = NULL; + return (&pvt->serv); + } + + cleanup: + if (pvt->serv.s_aliases) { + free(pvt->serv.s_aliases); + pvt->serv.s_aliases = NULL; + } + if (pvt->svbuf) { + free(pvt->svbuf); + pvt->svbuf = NULL; + } + return (NULL); +} + +static void +sv_minimize(struct irs_sv *this) { + UNUSED(this); + /* NOOP */ +} + +#ifdef SV_RES_SETGET +static struct __res_state * +sv_res_get(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + + return (__hesiod_res_get(dns->hes_ctx)); +} + +static void +sv_res_set(struct irs_sv *this, struct __res_state * res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + struct dns_p *dns = pvt->dns; + + __hesiod_res_set(dns->hes_ctx, res, free_res); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c b/usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c new file mode 100644 index 0000000000..9ca1c4bfe1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#include <netdb.h> +#include <port_after.h> + +#ifdef DO_PTHREADS +#include <pthread.h> +#include <stdlib.h> +#endif + +static const char *gai_errlist[] = { + "no error", + "address family not supported for name",/*%< EAI_ADDRFAMILY */ + "temporary failure", /*%< EAI_AGAIN */ + "invalid flags", /*%< EAI_BADFLAGS */ + "permanent failure", /*%< EAI_FAIL */ + "address family not supported", /*%< EAI_FAMILY */ + "memory failure", /*%< EAI_MEMORY */ + "no address", /*%< EAI_NODATA */ + "unknown name or service", /*%< EAI_NONAME */ + "service not supported for socktype", /*%< EAI_SERVICE */ + "socktype not supported", /*%< EAI_SOCKTYPE */ + "system failure", /*%< EAI_SYSTEM */ + "bad hints", /*%< EAI_BADHINTS */ + "bad protocol", /*%< EAI_PROTOCOL */ + "unknown error" /*%< Must be last. */ +}; + +static const int gai_nerr = (sizeof(gai_errlist)/sizeof(*gai_errlist)); + +#define EAI_BUFSIZE 128 + +const char * +gai_strerror(int ecode) { +#ifndef DO_PTHREADS + static char buf[EAI_BUFSIZE]; +#else /* DO_PTHREADS */ +#ifndef LIBBIND_MUTEX_INITIALIZER +#define LIBBIND_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif + static pthread_mutex_t lock = LIBBIND_MUTEX_INITIALIZER; + static pthread_key_t key; + static int once = 0; + char *buf; +#endif + + if (ecode >= 0 && ecode < (gai_nerr - 1)) + return (gai_errlist[ecode]); + +#ifdef DO_PTHREADS + if (!once) { + if (pthread_mutex_lock(&lock) != 0) + goto unknown; + if (!once) { + if (pthread_key_create(&key, free) != 0) { + (void)pthread_mutex_unlock(&lock); + goto unknown; + } + once = 1; + } + if (pthread_mutex_unlock(&lock) != 0) + goto unknown; + } + + buf = pthread_getspecific(key); + if (buf == NULL) { + buf = malloc(EAI_BUFSIZE); + if (buf == NULL) + goto unknown; + if (pthread_setspecific(key, buf) != 0) { + free(buf); + goto unknown; + } + } +#endif + /* + * XXX This really should be snprintf(buf, EAI_BUFSIZE, ...). + * It is safe until message catalogs are used. + */ + sprintf(buf, "%s: %d", gai_errlist[gai_nerr - 1], ecode); + return (buf); + +#ifdef DO_PTHREADS + unknown: + return ("unknown error"); +#endif +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen.c b/usr/src/lib/libresolv2_joy/common/irs/gen.c new file mode 100644 index 0000000000..98203149e9 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen.c @@ -0,0 +1,455 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief + * this is the top level dispatcher + * + * The dispatcher is implemented as an accessor class; it is an + * accessor class that calls other accessor classes, as controlled by a + * configuration file. + * + * A big difference between this accessor class and others is that the + * map class initializers are NULL, and the map classes are already + * filled in with method functions that will do the right thing. + */ + +/* Imports */ + +#include "port_before.h" + +#include <isc/assertions.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "gen_p.h" + +#ifdef SUNW_HOSTS_FALLBACK +extern int __res_no_hosts_fallback(void); +#endif /* SUNW_HOSTS_FALLBACK */ + +/* Definitions */ + +struct nameval { + const char * name; + int val; +}; + +static const struct nameval acc_names[irs_nacc+1] = { + { "local", irs_lcl }, + { "dns", irs_dns }, + { "nis", irs_nis }, + { "irp", irs_irp }, + { NULL, irs_nacc } +}; + +typedef struct irs_acc *(*accinit) __P((const char *options)); + +static const accinit accs[irs_nacc+1] = { + irs_lcl_acc, + irs_dns_acc, +#ifdef WANT_IRS_NIS + irs_nis_acc, +#else + NULL, +#endif + irs_irp_acc, + NULL +}; + +static const struct nameval map_names[irs_nmap+1] = { + { "group", irs_gr }, + { "passwd", irs_pw }, + { "services", irs_sv }, + { "protocols", irs_pr }, + { "hosts", irs_ho }, + { "networks", irs_nw }, + { "netgroup", irs_ng }, + { NULL, irs_nmap } +}; + +static const struct nameval option_names[] = { + { "merge", IRS_MERGE }, + { "continue", IRS_CONTINUE }, + { NULL, 0 } +}; + +/* Forward */ + +static void gen_close(struct irs_acc *); +static struct __res_state * gen_res_get(struct irs_acc *); +static void gen_res_set(struct irs_acc *, struct __res_state *, + void (*)(void *)); +static int find_name(const char *, const struct nameval nv[]); +static void init_map_rules(struct gen_p *, const char *conf_file); +static struct irs_rule *release_rule(struct irs_rule *); +static int add_rule(struct gen_p *, + enum irs_map_id, enum irs_acc_id, + const char *); + +/* Public */ + +struct irs_acc * +irs_gen_acc(const char *options, const char *conf_file) { + struct irs_acc *acc; + struct gen_p *irs; + + if (!(acc = memget(sizeof *acc))) { + errno = ENOMEM; + return (NULL); + } + memset(acc, 0x5e, sizeof *acc); + if (!(irs = memget(sizeof *irs))) { + errno = ENOMEM; + memput(acc, sizeof *acc); + return (NULL); + } + memset(irs, 0x5e, sizeof *irs); + irs->options = strdup(options); + irs->res = NULL; + irs->free_res = NULL; + memset(irs->accessors, 0, sizeof irs->accessors); + memset(irs->map_rules, 0, sizeof irs->map_rules); + init_map_rules(irs, conf_file); + acc->private = irs; +#ifdef WANT_IRS_GR + acc->gr_map = irs_gen_gr; +#else + acc->gr_map = NULL; +#endif +#ifdef WANT_IRS_PW + acc->pw_map = irs_gen_pw; +#else + acc->pw_map = NULL; +#endif + acc->sv_map = irs_gen_sv; + acc->pr_map = irs_gen_pr; + acc->ho_map = irs_gen_ho; + acc->nw_map = irs_gen_nw; + acc->ng_map = irs_gen_ng; + acc->res_get = gen_res_get; + acc->res_set = gen_res_set; + acc->close = gen_close; + return (acc); +} + +/* Methods */ + +static struct __res_state * +gen_res_get(struct irs_acc *this) { + struct gen_p *irs = (struct gen_p *)this->private; + + if (irs->res == NULL) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (res == NULL) + return (NULL); + memset(res, 0, sizeof *res); + gen_res_set(this, res, free); + } + + if (((irs->res->options & RES_INIT) == 0U) && res_ninit(irs->res) < 0) + return (NULL); + + return (irs->res); +} + +static void +gen_res_set(struct irs_acc *this, struct __res_state *res, + void (*free_res)(void *)) { + struct gen_p *irs = (struct gen_p *)this->private; +#if 0 + struct irs_rule *rule; + struct irs_ho *ho; + struct irs_nw *nw; +#endif + + if (irs->res && irs->free_res) { + res_nclose(irs->res); + (*irs->free_res)(irs->res); + } + + irs->res = res; + irs->free_res = free_res; + +#if 0 + for (rule = irs->map_rules[irs_ho]; rule; rule = rule->next) { + ho = rule->inst->ho; + + (*ho->res_set)(ho, res, NULL); + } + for (rule = irs->map_rules[irs_nw]; rule; rule = rule->next) { + nw = rule->inst->nw; + + (*nw->res_set)(nw, res, NULL); + } +#endif +} + +static void +gen_close(struct irs_acc *this) { + struct gen_p *irs = (struct gen_p *)this->private; + int n; + + /* Search rules. */ + for (n = 0; n < irs_nmap; n++) + while (irs->map_rules[n] != NULL) + irs->map_rules[n] = release_rule(irs->map_rules[n]); + + /* Access methods. */ + for (n = 0; n < irs_nacc; n++) { + /* Map objects. */ + if (irs->accessors[n].gr != NULL) + (*irs->accessors[n].gr->close)(irs->accessors[n].gr); + if (irs->accessors[n].pw != NULL) + (*irs->accessors[n].pw->close)(irs->accessors[n].pw); + if (irs->accessors[n].sv != NULL) + (*irs->accessors[n].sv->close)(irs->accessors[n].sv); + if (irs->accessors[n].pr != NULL) + (*irs->accessors[n].pr->close)(irs->accessors[n].pr); + if (irs->accessors[n].ho != NULL) + (*irs->accessors[n].ho->close)(irs->accessors[n].ho); + if (irs->accessors[n].nw != NULL) + (*irs->accessors[n].nw->close)(irs->accessors[n].nw); + if (irs->accessors[n].ng != NULL) + (*irs->accessors[n].ng->close)(irs->accessors[n].ng); + /* Enclosing accessor. */ + if (irs->accessors[n].acc != NULL) + (*irs->accessors[n].acc->close)(irs->accessors[n].acc); + } + + /* The options string was strdup'd. */ + free((void*)irs->options); + + if (irs->res && irs->free_res) + (*irs->free_res)(irs->res); + + /* The private data container. */ + memput(irs, sizeof *irs); + + /* The object. */ + memput(this, sizeof *this); +} + +/* Private */ + +static int +find_name(const char *name, const struct nameval names[]) { + int n; + + for (n = 0; names[n].name != NULL; n++) + if (strcmp(name, names[n].name) == 0) + return (names[n].val); + return (-1); +} + +static struct irs_rule * +release_rule(struct irs_rule *rule) { + struct irs_rule *next = rule->next; + + memput(rule, sizeof *rule); + return (next); +} + +static int +add_rule(struct gen_p *irs, + enum irs_map_id map, enum irs_acc_id acc, + const char *options) +{ + struct irs_rule **rules, *last, *tmp, *new; + struct irs_inst *inst; + const char *cp; + int n; + +#ifndef WANT_IRS_GR + if (map == irs_gr) + return (-1); +#endif +#ifndef WANT_IRS_PW + if (map == irs_pw) + return (-1); +#endif +#ifndef WANT_IRS_NIS + if (acc == irs_nis) + return (-1); +#endif + new = memget(sizeof *new); + if (new == NULL) + return (-1); + memset(new, 0x5e, sizeof *new); + new->next = NULL; + + new->inst = &irs->accessors[acc]; + + new->flags = 0; + cp = options; + while (cp && *cp) { + char option[50], *next; + + next = strchr(cp, ','); + if (next) + n = next++ - cp; + else + n = strlen(cp); + if ((size_t)n > sizeof option - 1) + n = sizeof option - 1; + strncpy(option, cp, n); + option[n] = '\0'; + + n = find_name(option, option_names); + if (n >= 0) + new->flags |= n; + + cp = next; + } + + rules = &irs->map_rules[map]; + for (last = NULL, tmp = *rules; + tmp != NULL; + last = tmp, tmp = tmp->next) + (void)NULL; + if (last == NULL) + *rules = new; + else + last->next = new; + + /* Try to instantiate map accessors for this if necessary & approp. */ + inst = &irs->accessors[acc]; + if (inst->acc == NULL && accs[acc] != NULL) + inst->acc = (*accs[acc])(irs->options); + if (inst->acc != NULL) { + if (inst->gr == NULL && inst->acc->gr_map != NULL) + inst->gr = (*inst->acc->gr_map)(inst->acc); + if (inst->pw == NULL && inst->acc->pw_map != NULL) + inst->pw = (*inst->acc->pw_map)(inst->acc); + if (inst->sv == NULL && inst->acc->sv_map != NULL) + inst->sv = (*inst->acc->sv_map)(inst->acc); + if (inst->pr == NULL && inst->acc->pr_map != NULL) + inst->pr = (*inst->acc->pr_map)(inst->acc); + if (inst->ho == NULL && inst->acc->ho_map != NULL) + inst->ho = (*inst->acc->ho_map)(inst->acc); + if (inst->nw == NULL && inst->acc->nw_map != NULL) + inst->nw = (*inst->acc->nw_map)(inst->acc); + if (inst->ng == NULL && inst->acc->ng_map != NULL) + inst->ng = (*inst->acc->ng_map)(inst->acc); + } + + return (0); +} + +static void +default_map_rules(struct gen_p *irs) { + /* Install time honoured and proved BSD style rules as default. */ + add_rule(irs, irs_gr, irs_lcl, ""); + add_rule(irs, irs_pw, irs_lcl, ""); + add_rule(irs, irs_sv, irs_lcl, ""); + add_rule(irs, irs_pr, irs_lcl, ""); +#ifdef SUNW_HOSTS_FALLBACK + if (__res_no_hosts_fallback()) + add_rule(irs, irs_ho, irs_dns, ""); + else { + add_rule(irs, irs_ho, irs_dns, "continue"); + add_rule(irs, irs_ho, irs_lcl, ""); + } +#else /* SUNW_HOSTS_FALLBACK */ + add_rule(irs, irs_ho, irs_dns, "continue"); + add_rule(irs, irs_ho, irs_lcl, ""); +#endif /* SUNW_HOSTS_FALLBACK */ + add_rule(irs, irs_nw, irs_dns, "continue"); + add_rule(irs, irs_nw, irs_lcl, ""); + add_rule(irs, irs_ng, irs_lcl, ""); +} + +static void +init_map_rules(struct gen_p *irs, const char *conf_file) { + char line[1024], pattern[40], mapname[20], accname[20], options[100]; + FILE *conf; + +#ifdef SUNW_HOSTS_FALLBACK + if (__res_no_hosts_fallback()) { + default_map_rules(irs); + return; + } +#endif /* SUNW_HOSTS_FALLBACK */ + + if (conf_file == NULL) + conf_file = _PATH_IRS_CONF ; + + /* A conf file of "" means compiled in defaults. Irpd wants this */ + if (conf_file[0] == '\0' || (conf = fopen(conf_file, "r")) == NULL) { + default_map_rules(irs); + return; + } + (void) sprintf(pattern, "%%%lus %%%lus %%%lus\n", + (unsigned long)sizeof mapname, + (unsigned long)sizeof accname, + (unsigned long)sizeof options); + while (fgets(line, sizeof line, conf)) { + enum irs_map_id map; + enum irs_acc_id acc; + char *tmp; + int n; + + for (tmp = line; + isascii((unsigned char)*tmp) && + isspace((unsigned char)*tmp); + tmp++) + (void)NULL; + if (*tmp == '#' || *tmp == '\n' || *tmp == '\0') + continue; + n = sscanf(tmp, pattern, mapname, accname, options); + if (n < 2) + continue; + if (n < 3) + options[0] = '\0'; + + n = find_name(mapname, map_names); + INSIST(n < irs_nmap); + if (n < 0) + continue; + map = (enum irs_map_id) n; + + n = find_name(accname, acc_names); + INSIST(n < irs_nacc); + if (n < 0) + continue; + acc = (enum irs_acc_id) n; + + add_rule(irs, map, acc, options); + } + fclose(conf); +} diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c b/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c new file mode 100644 index 0000000000..f8eddd4c0e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <stdlib.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "gen_p.h" + +/* Definitions */ + +struct pvt { + struct irs_rule * rules; + struct irs_rule * rule; + struct irs_ho * ho; + struct __res_state * res; + void (*free_res)(void *); +}; + +/* Forwards */ + +static void ho_close(struct irs_ho *this); +static struct hostent * ho_byname(struct irs_ho *this, const char *name); +static struct hostent * ho_byname2(struct irs_ho *this, const char *name, + int af); +static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, + int len, int af); +static struct hostent * ho_next(struct irs_ho *this); +static void ho_rewind(struct irs_ho *this); +static void ho_minimize(struct irs_ho *this); +static struct __res_state * ho_res_get(struct irs_ho *this); +static void ho_res_set(struct irs_ho *this, + struct __res_state *res, + void (*free_res)(void *)); +static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, + const struct addrinfo *pai); + +static int init(struct irs_ho *this); + +/* Exports */ + +struct irs_ho * +irs_gen_ho(struct irs_acc *this) { + struct gen_p *accpvt = (struct gen_p *)this->private; + struct irs_ho *ho; + struct pvt *pvt; + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + if (!(ho = memget(sizeof *ho))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(ho, 0x5e, sizeof *ho); + pvt->rules = accpvt->map_rules[irs_ho]; + pvt->rule = pvt->rules; + ho->private = pvt; + ho->close = ho_close; + ho->byname = ho_byname; + ho->byname2 = ho_byname2; + ho->byaddr = ho_byaddr; + ho->next = ho_next; + ho->rewind = ho_rewind; + ho->minimize = ho_minimize; + ho->res_get = ho_res_get; + ho->res_set = ho_res_set; + ho->addrinfo = ho_addrinfo; + return (ho); +} + +/* Methods. */ + +static void +ho_close(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + ho_minimize(this); + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct hostent * +ho_byname(struct irs_ho *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct hostent *rval; + struct irs_ho *ho; + int therrno = NETDB_INTERNAL; + int softerror = 0; + + if (init(this) == -1) + return (NULL); + + for (rule = pvt->rules; rule; rule = rule->next) { + ho = rule->inst->ho; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = 0; + rval = (*ho->byname)(ho, name); + if (rval != NULL) + return (rval); + if (softerror == 0 && + pvt->res->res_h_errno != HOST_NOT_FOUND && + pvt->res->res_h_errno != NETDB_INTERNAL) { + softerror = 1; + therrno = pvt->res->res_h_errno; + } + if (rule->flags & IRS_CONTINUE) + continue; + /* + * The value TRY_AGAIN can mean that the service + * is not available, or just that this particular name + * cannot be resolved now. We use the errno ECONNREFUSED + * to distinguish. If a lookup sets that errno when + * H_ERRNO is TRY_AGAIN, we continue to try other lookup + * functions, otherwise we return the TRY_AGAIN error. + */ + if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED) + break; + } + if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND) + RES_SET_H_ERRNO(pvt->res, therrno); + return (NULL); +} + +static struct hostent * +ho_byname2(struct irs_ho *this, const char *name, int af) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct hostent *rval; + struct irs_ho *ho; + int therrno = NETDB_INTERNAL; + int softerror = 0; + + if (init(this) == -1) + return (NULL); + + for (rule = pvt->rules; rule; rule = rule->next) { + ho = rule->inst->ho; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = 0; + rval = (*ho->byname2)(ho, name, af); + if (rval != NULL) + return (rval); + if (softerror == 0 && + pvt->res->res_h_errno != HOST_NOT_FOUND && + pvt->res->res_h_errno != NETDB_INTERNAL) { + softerror = 1; + therrno = pvt->res->res_h_errno; + } + if (rule->flags & IRS_CONTINUE) + continue; + /* + * See the comments in ho_byname() explaining + * the interpretation of TRY_AGAIN and ECONNREFUSED. + */ + if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED) + break; + } + if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND) + RES_SET_H_ERRNO(pvt->res, therrno); + return (NULL); +} + +static struct hostent * +ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct hostent *rval; + struct irs_ho *ho; + int therrno = NETDB_INTERNAL; + int softerror = 0; + + + if (init(this) == -1) + return (NULL); + + for (rule = pvt->rules; rule; rule = rule->next) { + ho = rule->inst->ho; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = 0; + rval = (*ho->byaddr)(ho, addr, len, af); + if (rval != NULL) + return (rval); + if (softerror == 0 && + pvt->res->res_h_errno != HOST_NOT_FOUND && + pvt->res->res_h_errno != NETDB_INTERNAL) { + softerror = 1; + therrno = pvt->res->res_h_errno; + } + + if (rule->flags & IRS_CONTINUE) + continue; + /* + * See the comments in ho_byname() explaining + * the interpretation of TRY_AGAIN and ECONNREFUSED. + */ + if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED) + break; + } + if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND) + RES_SET_H_ERRNO(pvt->res, therrno); + return (NULL); +} + +static struct hostent * +ho_next(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *rval; + struct irs_ho *ho; + + while (pvt->rule) { + ho = pvt->rule->inst->ho; + rval = (*ho->next)(ho); + if (rval) + return (rval); + if (!(pvt->rule->flags & IRS_CONTINUE)) + break; + pvt->rule = pvt->rule->next; + if (pvt->rule) { + ho = pvt->rule->inst->ho; + (*ho->rewind)(ho); + } + } + return (NULL); +} + +static void +ho_rewind(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_ho *ho; + + pvt->rule = pvt->rules; + if (pvt->rule) { + ho = pvt->rule->inst->ho; + (*ho->rewind)(ho); + } +} + +static void +ho_minimize(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + if (pvt->res) + res_nclose(pvt->res); + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_ho *ho = rule->inst->ho; + + (*ho->minimize)(ho); + } +} + +static struct __res_state * +ho_res_get(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + ho_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +ho_res_set(struct irs_ho *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_ho *ho = rule->inst->ho; + + (*ho->res_set)(ho, pvt->res, NULL); + } +} + +static struct addrinfo * +ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) +{ + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct addrinfo *rval = NULL; + struct irs_ho *ho; + int therrno = NETDB_INTERNAL; + int softerror = 0; + + if (init(this) == -1) + return (NULL); + + for (rule = pvt->rules; rule; rule = rule->next) { + ho = rule->inst->ho; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + errno = 0; + if (ho->addrinfo == NULL) /*%< for safety */ + continue; + rval = (*ho->addrinfo)(ho, name, pai); + if (rval != NULL) + return (rval); + if (softerror == 0 && + pvt->res->res_h_errno != HOST_NOT_FOUND && + pvt->res->res_h_errno != NETDB_INTERNAL) { + softerror = 1; + therrno = pvt->res->res_h_errno; + } + if (rule->flags & IRS_CONTINUE) + continue; + /* + * See the comments in ho_byname() explaining + * the interpretation of TRY_AGAIN and ECONNREFUSED. + */ + if (pvt->res->res_h_errno != TRY_AGAIN || + errno != ECONNREFUSED) + break; + } + if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND) + RES_SET_H_ERRNO(pvt->res, therrno); + return (NULL); +} + +static int +init(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res && !ho_res_get(this)) + return (-1); + + if (((pvt->res->options & RES_INIT) == 0U) && + (res_ninit(pvt->res) == -1)) + return (-1); + + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c b/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c new file mode 100644 index 0000000000..05c7d4dfb7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "gen_p.h" + +/* Types */ + +struct pvt { + struct irs_rule * rules; + struct irs_rule * rule; + char * curgroup; +}; + +/* Forward */ + +static void ng_close(struct irs_ng *); +static int ng_next(struct irs_ng *, const char **, + const char **, const char **); +static int ng_test(struct irs_ng *, const char *, + const char *, const char *, + const char *); +static void ng_rewind(struct irs_ng *, const char *); +static void ng_minimize(struct irs_ng *); + +/* Public */ + +struct irs_ng * +irs_gen_ng(struct irs_acc *this) { + struct gen_p *accpvt = (struct gen_p *)this->private; + struct irs_ng *ng; + struct pvt *pvt; + + if (!(ng = memget(sizeof *ng))) { + errno = ENOMEM; + return (NULL); + } + memset(ng, 0x5e, sizeof *ng); + if (!(pvt = memget(sizeof *pvt))) { + memput(ng, sizeof *ng); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->rules = accpvt->map_rules[irs_ng]; + pvt->rule = pvt->rules; + ng->private = pvt; + ng->close = ng_close; + ng->next = ng_next; + ng->test = ng_test; + ng->rewind = ng_rewind; + ng->minimize = ng_minimize; + return (ng); +} + +/* Methods */ + +static void +ng_close(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + + ng_minimize(this); + if (pvt->curgroup) + free(pvt->curgroup); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static int +ng_next(struct irs_ng *this, const char **host, const char **user, + const char **domain) +{ + struct pvt *pvt = (struct pvt *)this->private; + struct irs_ng *ng; + + while (pvt->rule) { + ng = pvt->rule->inst->ng; + if ((*ng->next)(ng, host, user, domain) == 1) + return (1); + if (!(pvt->rule->flags & IRS_CONTINUE)) + break; + pvt->rule = pvt->rule->next; + if (pvt->rule) { + ng = pvt->rule->inst->ng; + (*ng->rewind)(ng, pvt->curgroup); + } + } + return (0); +} + +static int +ng_test(struct irs_ng *this, const char *name, + const char *user, const char *host, const char *domain) +{ + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct irs_ng *ng; + int rval; + + rval = 0; + for (rule = pvt->rules; rule; rule = rule->next) { + ng = rule->inst->ng; + rval = (*ng->test)(ng, name, user, host, domain); + if (rval || !(rule->flags & IRS_CONTINUE)) + break; + } + return (rval); +} + +static void +ng_rewind(struct irs_ng *this, const char *group) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_ng *ng; + + pvt->rule = pvt->rules; + if (pvt->rule) { + if (pvt->curgroup) + free(pvt->curgroup); + pvt->curgroup = strdup(group); + ng = pvt->rule->inst->ng; + (*ng->rewind)(ng, pvt->curgroup); + } +} + +static void +ng_minimize(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_ng *ng = rule->inst->ng; + + (*ng->minimize)(ng); + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_nw.c b/usr/src/lib/libresolv2_joy/common/irs/gen_nw.c new file mode 100644 index 0000000000..a84a83af98 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_nw.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "gen_p.h" + +/* Types */ + +struct pvt { + struct irs_rule * rules; + struct irs_rule * rule; + struct __res_state * res; + void (*free_res)(void *); +}; + +/* Forward */ + +static void nw_close(struct irs_nw*); +static struct nwent * nw_next(struct irs_nw *); +static struct nwent * nw_byname(struct irs_nw *, const char *, int); +static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int); +static void nw_rewind(struct irs_nw *); +static void nw_minimize(struct irs_nw *); +static struct __res_state * nw_res_get(struct irs_nw *this); +static void nw_res_set(struct irs_nw *this, + struct __res_state *res, + void (*free_res)(void *)); + +static int init(struct irs_nw *this); + +/* Public */ + +struct irs_nw * +irs_gen_nw(struct irs_acc *this) { + struct gen_p *accpvt = (struct gen_p *)this->private; + struct irs_nw *nw; + struct pvt *pvt; + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + if (!(nw = memget(sizeof *nw))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(nw, 0x5e, sizeof *nw); + pvt->rules = accpvt->map_rules[irs_nw]; + pvt->rule = pvt->rules; + nw->private = pvt; + nw->close = nw_close; + nw->next = nw_next; + nw->byname = nw_byname; + nw->byaddr = nw_byaddr; + nw->rewind = nw_rewind; + nw->minimize = nw_minimize; + nw->res_get = nw_res_get; + nw->res_set = nw_res_set; + return (nw); +} + +/* Methods */ + +static void +nw_close(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + nw_minimize(this); + + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct nwent * +nw_next(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct nwent *rval; + struct irs_nw *nw; + + if (init(this) == -1) + return(NULL); + + while (pvt->rule) { + nw = pvt->rule->inst->nw; + rval = (*nw->next)(nw); + if (rval) + return (rval); + if (!(pvt->rules->flags & IRS_CONTINUE)) + break; + pvt->rule = pvt->rule->next; + if (pvt->rule) { + nw = pvt->rule->inst->nw; + (*nw->rewind)(nw); + } + } + return (NULL); +} + +static struct nwent * +nw_byname(struct irs_nw *this, const char *name, int type) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct nwent *rval; + struct irs_nw *nw; + + if (init(this) == -1) + return(NULL); + + for (rule = pvt->rules; rule; rule = rule->next) { + nw = rule->inst->nw; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + rval = (*nw->byname)(nw, name, type); + if (rval != NULL) + return (rval); + if (pvt->res->res_h_errno != TRY_AGAIN && + !(rule->flags & IRS_CONTINUE)) + break; + } + return (NULL); +} + +static struct nwent * +nw_byaddr(struct irs_nw *this, void *net, int length, int type) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct nwent *rval; + struct irs_nw *nw; + + if (init(this) == -1) + return(NULL); + + for (rule = pvt->rules; rule; rule = rule->next) { + nw = rule->inst->nw; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + rval = (*nw->byaddr)(nw, net, length, type); + if (rval != NULL) + return (rval); + if (pvt->res->res_h_errno != TRY_AGAIN && + !(rule->flags & IRS_CONTINUE)) + break; + } + return (NULL); +} + +static void +nw_rewind(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_nw *nw; + + pvt->rule = pvt->rules; + if (pvt->rule) { + nw = pvt->rule->inst->nw; + (*nw->rewind)(nw); + } +} + +static void +nw_minimize(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + if (pvt->res) + res_nclose(pvt->res); + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_nw *nw = rule->inst->nw; + + (*nw->minimize)(nw); + } +} + +static struct __res_state * +nw_res_get(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + nw_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +nw_res_set(struct irs_nw *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_nw *nw = rule->inst->nw; + + (*nw->res_set)(nw, pvt->res, NULL); + } +} + +static int +init(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res && !nw_res_get(this)) + return (-1); + if (((pvt->res->options & RES_INIT) == 0U) && + res_ninit(pvt->res) == -1) + return (-1); + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_p.h b/usr/src/lib/libresolv2_joy/common/irs/gen_p.h new file mode 100644 index 0000000000..1adc5909bb --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_p.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: gen_p.h,v 1.3 2005/04/27 04:56:23 sra Exp $ + */ + +/*! \file + * Notes: + * We hope to create a complete set of thread-safe entry points someday, + * which will mean a set of getXbyY() functions that take as an argument + * a pointer to the map class, which will have a pointer to the private + * data, which will be used preferentially to the static variables that + * are necessary to support the "classic" interface. This "classic" + * interface will then be reimplemented as stubs on top of the thread + * safe modules, and will keep the map class pointers as their only + * static data. HOWEVER, we are not there yet. So while we will call + * the just-barely-converted map class methods with map class pointers, + * right now they probably all still use statics. We're not fooling + * anybody, and we're not trying to (yet). + */ + +#ifndef _GEN_P_H_INCLUDED +#define _GEN_P_H_INCLUDED + +/*% + * These are the access methods. + */ +enum irs_acc_id { + irs_lcl, /*%< Local. */ + irs_dns, /*%< DNS or Hesiod. */ + irs_nis, /*%< Sun NIS ("YP"). */ + irs_irp, /*%< IR protocol. */ + irs_nacc +}; + +/*% + * These are the map types. + */ +enum irs_map_id { + irs_gr, /*%< "group" */ + irs_pw, /*%< "passwd" */ + irs_sv, /*%< "services" */ + irs_pr, /*%< "protocols" */ + irs_ho, /*%< "hosts" */ + irs_nw, /*%< "networks" */ + irs_ng, /*%< "netgroup" */ + irs_nmap +}; + +/*% + * This is an accessor instance. + */ +struct irs_inst { + struct irs_acc *acc; + struct irs_gr * gr; + struct irs_pw * pw; + struct irs_sv * sv; + struct irs_pr * pr; + struct irs_ho * ho; + struct irs_nw * nw; + struct irs_ng * ng; +}; + +/*% + * This is a search rule for some map type. + */ +struct irs_rule { + struct irs_rule * next; + struct irs_inst * inst; + int flags; +}; +#define IRS_MERGE 0x0001 /*%< Don't stop if acc. has data? */ +#define IRS_CONTINUE 0x0002 /*%< Don't stop if acc. has no data? */ +/* + * This is the private data for a search access class. + */ +struct gen_p { + char * options; + struct irs_rule * map_rules[(int)irs_nmap]; + struct irs_inst accessors[(int)irs_nacc]; + struct __res_state * res; + void (*free_res) __P((void *)); +}; + +/* + * Externs. + */ + +extern struct irs_acc * irs_gen_acc __P((const char *, const char *conf_file)); +extern struct irs_gr * irs_gen_gr __P((struct irs_acc *)); +extern struct irs_pw * irs_gen_pw __P((struct irs_acc *)); +extern struct irs_sv * irs_gen_sv __P((struct irs_acc *)); +extern struct irs_pr * irs_gen_pr __P((struct irs_acc *)); +extern struct irs_ho * irs_gen_ho __P((struct irs_acc *)); +extern struct irs_nw * irs_gen_nw __P((struct irs_acc *)); +extern struct irs_ng * irs_gen_ng __P((struct irs_acc *)); + +#endif /*_IRS_P_H_INCLUDED*/ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c b/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c new file mode 100644 index 0000000000..0349debe44 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "gen_p.h" + +/* Types */ + +struct pvt { + struct irs_rule * rules; + struct irs_rule * rule; + struct __res_state * res; + void (*free_res)(void *); +}; + +/* Forward */ + +static void pr_close(struct irs_pr*); +static struct protoent * pr_next(struct irs_pr *); +static struct protoent * pr_byname(struct irs_pr *, const char *); +static struct protoent * pr_bynumber(struct irs_pr *, int); +static void pr_rewind(struct irs_pr *); +static void pr_minimize(struct irs_pr *); +static struct __res_state * pr_res_get(struct irs_pr *); +static void pr_res_set(struct irs_pr *, + struct __res_state *, + void (*)(void *)); + +/* Public */ + +struct irs_pr * +irs_gen_pr(struct irs_acc *this) { + struct gen_p *accpvt = (struct gen_p *)this->private; + struct irs_pr *pr; + struct pvt *pvt; + + if (!(pr = memget(sizeof *pr))) { + errno = ENOMEM; + return (NULL); + } + memset(pr, 0x5e, sizeof *pr); + if (!(pvt = memget(sizeof *pvt))) { + memput(pr, sizeof *pr); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->rules = accpvt->map_rules[irs_pr]; + pvt->rule = pvt->rules; + pr->private = pvt; + pr->close = pr_close; + pr->next = pr_next; + pr->byname = pr_byname; + pr->bynumber = pr_bynumber; + pr->rewind = pr_rewind; + pr->minimize = pr_minimize; + pr->res_get = pr_res_get; + pr->res_set = pr_res_set; + return (pr); +} + +/* Methods */ + +static void +pr_close(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct protoent * +pr_next(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct protoent *rval; + struct irs_pr *pr; + + while (pvt->rule) { + pr = pvt->rule->inst->pr; + rval = (*pr->next)(pr); + if (rval) + return (rval); + if (!(pvt->rules->flags & IRS_CONTINUE)) + break; + pvt->rule = pvt->rule->next; + if (pvt->rule) { + pr = pvt->rule->inst->pr; + (*pr->rewind)(pr); + } + } + return (NULL); +} + +static struct protoent * +pr_byname(struct irs_pr *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct protoent *rval; + struct irs_pr *pr; + + rval = NULL; + for (rule = pvt->rules; rule; rule = rule->next) { + pr = rule->inst->pr; + rval = (*pr->byname)(pr, name); + if (rval || !(rule->flags & IRS_CONTINUE)) + break; + } + return (rval); +} + +static struct protoent * +pr_bynumber(struct irs_pr *this, int proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct protoent *rval; + struct irs_pr *pr; + + rval = NULL; + for (rule = pvt->rules; rule; rule = rule->next) { + pr = rule->inst->pr; + rval = (*pr->bynumber)(pr, proto); + if (rval || !(rule->flags & IRS_CONTINUE)) + break; + } + return (rval); +} + +static void +pr_rewind(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_pr *pr; + + pvt->rule = pvt->rules; + if (pvt->rule) { + pr = pvt->rule->inst->pr; + (*pr->rewind)(pr); + } +} + +static void +pr_minimize(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_pr *pr = rule->inst->pr; + + (*pr->minimize)(pr); + } +} + +static struct __res_state * +pr_res_get(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + pr_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +pr_res_set(struct irs_pr *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_pr *pr = rule->inst->pr; + + if (pr->res_set) + (*pr->res_set)(pr, pvt->res, NULL); + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c b/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c new file mode 100644 index 0000000000..a6f29d2a0f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "gen_p.h" + +/* Types */ + +struct pvt { + struct irs_rule * rules; + struct irs_rule * rule; + struct __res_state * res; + void (*free_res)(void *); +}; + +/* Forward */ + +static void sv_close(struct irs_sv*); +static struct servent * sv_next(struct irs_sv *); +static struct servent * sv_byname(struct irs_sv *, const char *, + const char *); +static struct servent * sv_byport(struct irs_sv *, int, const char *); +static void sv_rewind(struct irs_sv *); +static void sv_minimize(struct irs_sv *); +static struct __res_state * sv_res_get(struct irs_sv *); +static void sv_res_set(struct irs_sv *, + struct __res_state *, + void (*)(void *)); + +/* Public */ + +struct irs_sv * +irs_gen_sv(struct irs_acc *this) { + struct gen_p *accpvt = (struct gen_p *)this->private; + struct irs_sv *sv; + struct pvt *pvt; + + if (!(sv = memget(sizeof *sv))) { + errno = ENOMEM; + return (NULL); + } + memset(sv, 0x5e, sizeof *sv); + if (!(pvt = memget(sizeof *pvt))) { + memput(sv, sizeof *sv); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->rules = accpvt->map_rules[irs_sv]; + pvt->rule = pvt->rules; + sv->private = pvt; + sv->close = sv_close; + sv->next = sv_next; + sv->byname = sv_byname; + sv->byport = sv_byport; + sv->rewind = sv_rewind; + sv->minimize = sv_minimize; + sv->res_get = sv_res_get; + sv->res_set = sv_res_set; + return (sv); +} + +/* Methods */ + +static void +sv_close(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct servent * +sv_next(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct servent *rval; + struct irs_sv *sv; + + while (pvt->rule) { + sv = pvt->rule->inst->sv; + rval = (*sv->next)(sv); + if (rval) + return (rval); + if (!(pvt->rule->flags & IRS_CONTINUE)) + break; + pvt->rule = pvt->rule->next; + if (pvt->rule) { + sv = pvt->rule->inst->sv; + (*sv->rewind)(sv); + } + } + return (NULL); +} + +static struct servent * +sv_byname(struct irs_sv *this, const char *name, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct servent *rval; + struct irs_sv *sv; + + rval = NULL; + for (rule = pvt->rules; rule; rule = rule->next) { + sv = rule->inst->sv; + rval = (*sv->byname)(sv, name, proto); + if (rval || !(rule->flags & IRS_CONTINUE)) + break; + } + return (rval); +} + +static struct servent * +sv_byport(struct irs_sv *this, int port, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + struct servent *rval; + struct irs_sv *sv; + + rval = NULL; + for (rule = pvt->rules; rule; rule = rule->next) { + sv = rule->inst->sv; + rval = (*sv->byport)(sv, port, proto); + if (rval || !(rule->flags & IRS_CONTINUE)) + break; + } + return (rval); +} + +static void +sv_rewind(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_sv *sv; + + pvt->rule = pvt->rules; + if (pvt->rule) { + sv = pvt->rule->inst->sv; + (*sv->rewind)(sv); + } +} + +static void +sv_minimize(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_sv *sv = rule->inst->sv; + + (*sv->minimize)(sv); + } +} + +static struct __res_state * +sv_res_get(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + sv_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +sv_res_set(struct irs_sv *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + struct irs_rule *rule; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; + + for (rule = pvt->rules; rule != NULL; rule = rule->next) { + struct irs_sv *sv = rule->inst->sv; + + if (sv->res_set) + (*sv->res_set)(sv, pvt->res, NULL); + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c b/usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c new file mode 100644 index 0000000000..19bbbea854 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c @@ -0,0 +1,1253 @@ +/* $KAME: getaddrinfo.c,v 1.14 2001/01/06 09:41:15 jinmei 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. + */ + +/*! \file + * Issues to be discussed: + *\li Thread safe-ness must be checked. + *\li 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. + *\li IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2 + * says to use inet_aton() to convert IPv4 numeric to binary (allows + * classful form as a result). + * current code - disallow classful form for IPv4 (due to use of inet_pton). + *\li freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is + * invalid. + * current code - SEGV on freeaddrinfo(NULL) + * Note: + *\li We use getipnodebyname() just for thread-safeness. There's no intent + * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to + * getipnodebyname(). + *\li 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? + *\li (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? + * \par Additional Issue: + * 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 + * follows: + * \li The code makes use of following calls when asked to resolver with + * ai_family = PF_UNSPEC: + *\code getipnodebyname(host, AF_INET6); + * getipnodebyname(host, AF_INET); + *\endcode + * \li This will result in the following queries if the node is configure to + * prefer /etc/hosts than DNS: + *\code + * lookup /etc/hosts for IPv6 address + * lookup DNS for IPv6 address + * lookup /etc/hosts for IPv4 address + * lookup DNS for IPv4 address + *\endcode + * which may not meet people's requirement. + * \li The right thing to happen is to have underlying layer which does + * PF_UNSPEC lookup (lookup both) and return chain of addrinfos. + * This would result in a bit of code duplicate with _dns_ghbyname() and + * friends. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> + +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <netdb.h> +#include <resolv_joy.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#include <stdarg.h> + +#include <irs.h> +#include <isc/assertions.h> + +#include "port_after.h" + +#include "irs_data.h" + +#define SUCCESS 0 +#define ANY 0 +#define YES 1 +#define NO 0 + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in_loopback[] = { 127, 0, 0, 1 }; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; + +static const struct afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; + int a_scoped; +} afdl [] = { + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback, 1}, + {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 + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, + { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, + { -1, 0, 0, NULL, 0 }, +}; + +#define PTON_MAX 16 + +static int str_isnumber __P((const char *)); +static int explore_fqdn __P((const struct addrinfo *, const char *, + const char *, struct addrinfo **)); +static int explore_copy __P((const struct addrinfo *, const struct addrinfo *, + struct addrinfo **)); +static int explore_null __P((const struct addrinfo *, + const char *, struct addrinfo **)); +static int explore_numeric __P((const struct addrinfo *, const char *, + const char *, struct addrinfo **)); +static int explore_numeric_scope __P((const struct addrinfo *, const char *, + const char *, struct addrinfo **)); +static int get_canonname __P((const struct addrinfo *, + struct addrinfo *, const char *)); +static struct addrinfo *get_ai __P((const struct addrinfo *, + const struct afd *, const char *)); +static struct addrinfo *copy_ai __P((const struct addrinfo *)); +static int get_portmatch __P((const struct addrinfo *, const char *)); +static int get_port __P((const struct addrinfo *, const char *, int)); +static const struct afd *find_afd __P((int)); +static int addrconfig __P((int)); +static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *, + u_int32_t *scopeidp)); +static struct net_data *init __P((void)); + +struct addrinfo *hostent2addrinfo __P((struct hostent *, + const struct addrinfo *)); +struct addrinfo *addr2addrinfo __P((const struct addrinfo *, + const char *)); + +#if 0 +static const char *ai_errlist[] = { + "Success", + "Address family for hostname not supported", /*%< EAI_ADDRFAMILY */ + "Temporary failure in name resolution", /*%< EAI_AGAIN */ + "Invalid value for ai_flags", /*%< EAI_BADFLAGS */ + "Non-recoverable failure in name resolution", /*%< EAI_FAIL */ + "ai_family not supported", /*%< EAI_FAMILY */ + "Memory allocation failure", /*%< EAI_MEMORY */ + "No address associated with hostname", /*%< EAI_NODATA */ + "hostname nor servname provided, or not known", /*%< EAI_NONAME */ + "servname not supported for ai_socktype", /*%< EAI_SERVICE */ + "ai_socktype not supported", /*%< EAI_SOCKTYPE */ + "System error returned in errno", /*%< EAI_SYSTEM */ + "Invalid value for hints", /*%< EAI_BADHINTS */ + "Resolved protocol is unknown", /*%< EAI_PROTOCOL */ + "Unknown error", /*%< EAI_MAX */ +}; +#endif + +/* 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) + +#ifndef SOLARIS2 +#define SETERROR(err) \ +do { \ + /* external reference: error, and label bad */ \ + error = (err); \ + goto bad; \ + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) +#else +#define SETERROR(err) \ +do { \ + /* external reference: error, and label bad */ \ + error = (err); \ + if (error == error) \ + goto bad; \ +} while (/*CONSTCOND*/0) +#endif + + +#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))) + +#if 0 /*%< bind8 has its own version */ +char * +gai_strerror(ecode) + int ecode; +{ + if (ecode < 0 || ecode > EAI_MAX) + ecode = EAI_MAX; + return ai_errlist[ecode]; +} +#endif + +void +freeaddrinfo(ai) + 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 +str_isnumber(p) + const char *p; +{ + char *ep; + + if (*p == '\0') + return NO; + ep = NULL; + errno = 0; + (void)strtoul(p, &ep, 10); + if (errno == 0 && ep && *ep == '\0') + return YES; + else + return NO; +} + +int +getaddrinfo(hostname, servname, hints, res) + const char *hostname, *servname; + const struct addrinfo *hints; + struct addrinfo **res; +{ + struct addrinfo sentinel; + struct addrinfo *cur; + int error = 0; + struct addrinfo ai, ai0, *afai = NULL; + struct addrinfo *pai; + const struct explore *ex; + + 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; +#if defined(sun) && defined(_SOCKLEN_T) && defined(__sparcv9) + /* + * clear _ai_pad to preserve binary + * compatibility with previously compiled 64-bit + * applications in a pre-SUSv3 environment by + * guaranteeing the upper 32-bits are empty. + */ + pai->_ai_pad = 0; +#endif + 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) + SETERROR(EAI_BADHINTS); /*%< xxx */ + if (hints->ai_flags & ~AI_MASK) + SETERROR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: + case PF_INET6: + break; + default: + SETERROR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + +#if defined(sun) && defined(_SOCKLEN_T) && defined(__sparcv9) + /* + * We need to clear _ai_pad to preserve binary + * compatibility. See prior comment. + */ + pai->_ai_pad = 0; +#endif + /* + * 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) { + SETERROR(EAI_BADHINTS); + } + } + } + } + + /* + * post-2553: AI_ALL and AI_V4MAPPED are effective only against + * AF_INET6 query. They needs 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 */ + SETERROR(EAI_BADFLAGS); +#else + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; +#endif + } + + /* + * 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) + SETERROR(error); + + *pai = ai0; + } + + ai0 = *pai; + + /* NULL hostname, or numeric hostname */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + 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 the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + continue; + + if (hostname == NULL) { + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + if (!addrconfig(pai->ai_family)) + continue; + 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) + goto good; + + if (pai->ai_flags & AI_NUMERICHOST) + SETERROR(EAI_NONAME); + if (hostname == NULL) + SETERROR(EAI_NONAME); + + /* + * hostname as alphabetical name. + * We'll make sure that + * - if returning addrinfo list is empty, return non-zero error + * value (already known one or EAI_NONAME). + * - otherwise, + * + if we haven't had any errors, return 0 (i.e. success). + * + if we've had an error, free the list and return the error. + * without any assumption on the behavior of explore_fqdn(). + */ + + /* first, try to query DNS for all possible address families. */ + *pai = ai0; + error = explore_fqdn(pai, hostname, servname, &afai); + if (error) { + if (afai != NULL) + freeaddrinfo(afai); + goto free; + } + if (afai == NULL) { + error = EAI_NONAME; /*%< we've had no errors. */ + goto free; + } + + /* + * we would like to prefer AF_INET6 than AF_INET, so we'll make an + * outer loop by AFs. + */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = ex->e_af; + + 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; + } + +#ifdef AI_ADDRCONFIG + /* + * If AI_ADDRCONFIG is specified, check if we are + * expected to return the address family or not. + */ + if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && + !addrconfig(pai->ai_family)) + continue; +#endif + + 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 the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + continue; + + if ((error = explore_copy(pai, afai, &cur->ai_next)) != 0) { + freeaddrinfo(afai); + goto free; + } + + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + freeaddrinfo(afai); /*%< afai must not be NULL at this point. */ + + if (sentinel.ai_next) { +good: + *res = sentinel.ai_next; + return(SUCCESS); + } else { + /* + * All the process succeeded, but we've had an empty list. + * This can happen if the given hints do not match our + * candidates. + */ + error = EAI_NONAME; + } + +free: +bad: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + *res = NULL; + return(error); +} + +/*% + * FQDN hostname, DNS lookup + */ +static int +explore_fqdn(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ + struct addrinfo *result; + struct addrinfo *cur; + struct net_data *net_data = init(); + struct irs_ho *ho; + int error = 0; + char tmp[NS_MAXDNAME]; + const char *cp; + + INSIST(res != NULL && *res == NULL); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return(0); + + if (!net_data || !(ho = net_data->ho)) + return(0); +#if 0 /*%< XXX (notyet) */ + if (net_data->ho_stayopen && net_data->ho_last && + net_data->ho_last->h_addrtype == af) { + if (ns_samename(name, net_data->ho_last->h_name) == 1) + return (net_data->ho_last); + for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++) + if (ns_samename(name, *hap) == 1) + return (net_data->ho_last); + } +#endif + if (!strchr(hostname, '.') && + (cp = res_hostalias(net_data->res, hostname, + tmp, sizeof(tmp)))) + hostname = cp; + result = (*ho->addrinfo)(ho, hostname, pai); + if (!net_data->ho_stayopen) { + (*ho->minimize)(ho); + } + if (result == NULL) { + int e = h_errno; + + switch(e) { + case NETDB_INTERNAL: + error = EAI_SYSTEM; + break; + case TRY_AGAIN: + error = EAI_AGAIN; + break; + case NO_RECOVERY: + error = EAI_FAIL; + break; + case HOST_NOT_FOUND: + case NO_DATA: + error = EAI_NONAME; + break; + default: + case NETDB_SUCCESS: /*%< should be impossible... */ + error = EAI_NONAME; + break; + } + goto free; + } + + for (cur = result; cur; cur = cur->ai_next) { + GET_PORT(cur, servname); /*%< XXX: redundant lookups... */ + /* canonname should already be filled. */ + } + + *res = result; + + return(0); + +free: + if (result) + freeaddrinfo(result); + return error; +} + +static int +explore_copy(pai, src0, res) + const struct addrinfo *pai; /*%< seed */ + const struct addrinfo *src0; /*%< source */ + struct addrinfo **res; +{ + int error; + struct addrinfo sentinel, *cur; + const struct addrinfo *src; + + error = 0; + sentinel.ai_next = NULL; + cur = &sentinel; + + for (src = src0; src != NULL; src = src->ai_next) { + if (src->ai_family != pai->ai_family) + continue; + + cur->ai_next = copy_ai(src); + if (!cur->ai_next) { + error = EAI_MEMORY; + goto fail; + } + + cur->ai_next->ai_socktype = pai->ai_socktype; + cur->ai_next->ai_protocol = pai->ai_protocol; + cur = cur->ai_next; + } + + *res = sentinel.ai_next; + return 0; + +fail: + freeaddrinfo(sentinel.ai_next); + return error; +} + +/*% + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ +static int +explore_null(pai, servname, res) + const struct addrinfo *pai; + const char *servname; + struct addrinfo **res; +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + 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(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + char pton[PTON_MAX]; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + switch (afd->a_af) { +#if 0 /*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); + while (cur->ai_next) + cur = cur->ai_next; + } else + SETERROR(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); + while (cur->ai_next) + cur = cur->ai_next; + } else + SETERROR(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(pai, hostname, servname, res) + const struct addrinfo *pai; + const char *hostname; + const char *servname; + struct addrinfo **res; +{ +#ifndef SCOPE_DELIMITER + return explore_numeric(pai, hostname, servname, res); +#else + const struct afd *afd; + struct addrinfo *cur; + int error; + char *cp, *hostname2 = NULL, *scope, *addr; + struct sockaddr_in6 *sin6; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (!afd->a_scoped) + return explore_numeric(pai, hostname, servname, res); + + cp = strchr(hostname, SCOPE_DELIMITER); + if (cp == NULL) + return explore_numeric(pai, hostname, servname, res); + + /* + * Handle special case of <scoped_address><delimiter><scope id> + */ + 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); + if (error == 0) { + u_int32_t scopeid = 0; + + 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)) { + free(hostname2); + return(EAI_NONAME); /*%< XXX: is return OK? */ + } +#ifdef HAVE_SIN6_SCOPE_ID + sin6->sin6_scope_id = scopeid; +#endif + } + } + + free(hostname2); + + return error; +#endif +} + +static int +get_canonname(pai, ai, str) + const struct addrinfo *pai; + struct addrinfo *ai; + const char *str; +{ + if ((pai->ai_flags & AI_CANONNAME) != 0) { + ai->ai_canonname = (char *)malloc(strlen(str) + 1); + if (ai->ai_canonname == NULL) + return EAI_MEMORY; + strcpy(ai->ai_canonname, str); + } + return 0; +} + +static struct addrinfo * +get_ai(pai, afd, addr) + const struct addrinfo *pai; + const struct afd *afd; + const char *addr; +{ + char *p; + struct addrinfo *ai; + + 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); +#ifdef HAVE_SA_LEN + ai->ai_addr->sa_len = afd->a_socklen; +#endif + ai->ai_addrlen = afd->a_socklen; + ai->ai_addr->sa_family = ai->ai_family = afd->a_af; + p = (char *)(void *)(ai->ai_addr); + memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); + return ai; +} + +/* XXX need to malloc() the same way we do from other functions! */ +static struct addrinfo * +copy_ai(pai) + const struct addrinfo *pai; +{ + struct addrinfo *ai; + size_t l; + + l = sizeof(*ai) + pai->ai_addrlen; + if ((ai = (struct addrinfo *)malloc(l)) == NULL) + return NULL; + memset(ai, 0, l); + memcpy(ai, pai, sizeof(*ai)); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); + + if (pai->ai_canonname) { + l = strlen(pai->ai_canonname) + 1; + if ((ai->ai_canonname = malloc(l)) == NULL) { + free(ai); + return NULL; + } + strcpy(ai->ai_canonname, pai->ai_canonname); /* (checked) */ + } else { + /* just to make sure */ + ai->ai_canonname = NULL; + } + + ai->ai_next = NULL; + + 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((const struct addrinfo *)ai, servname, 1); +} + +static int +get_port(const 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: + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + allownumeric = 1; + break; + default: + allownumeric = 0; + break; + } + break; + default: + return EAI_SOCKTYPE; + } + + if (str_isnumber(servname)) { + if (!allownumeric) + return EAI_SERVICE; + port = atoi(servname); + if (port < 0 || port > 65535) + return EAI_SERVICE; + port = htons(port); + } else { + 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; + case AF_INET6: + ((struct sockaddr_in6 *)(void *) + ai->ai_addr)->sin6_port = port; + break; + } + } + + return 0; +} + +static const struct afd * +find_afd(af) + 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. + */ +static int +addrconfig(af) + int af; +{ + int s; + + /* XXX errno */ + s = socket(af, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + close(s); + return 1; +} + +/* convert a string to a scope identifier. XXX: IPv6 specific */ +static int +ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, + u_int32_t *scopeidp) +{ + u_int32_t scopeid; + u_long lscopeid; + struct in6_addr *a6 = &sin6->sin6_addr; + char *ep; + + /* empty scopeid portion is invalid */ + if (*scope == '\0') + return (0); + +#ifdef USE_IFNAMELINKID + if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || + IN6_IS_ADDR_MC_NODELOCAL(a6)) { + /* + * Using interface names as link indices can be allowed + * only when we can assume a one-to-one mappings between + * links and interfaces. See comments in getnameinfo.c. + */ + scopeid = if_nametoindex(scope); + if (scopeid == 0) + goto trynumeric; + *scopeidp = scopeid; + return (1); + } +#endif + + /* 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 = lscopeid & 0xffffffff; + if (errno == 0 && ep && *ep == '\0' && scopeid == lscopeid) { + *scopeidp = scopeid; + return (1); + } else + return (0); +} + +struct addrinfo * +hostent2addrinfo(hp, pai) + struct hostent *hp; + const struct addrinfo *pai; +{ + int i, af, error = 0; + char **aplist = NULL, *ap; + struct addrinfo sentinel, *cur; + const struct afd *afd; + + af = hp->h_addrtype; + if (pai->ai_family != AF_UNSPEC && af != pai->ai_family) + return(NULL); + + afd = find_afd(af); + if (afd == NULL) + return(NULL); + + aplist = hp->h_addr_list; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + for (i = 0; (ap = aplist[i]) != NULL; i++) { +#if 0 /*%< the trick seems too much */ + af = hp->h_addr_list; + if (af == AF_INET6 && + IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { + af = AF_INET; + ap = ap + sizeof(struct in6_addr) + - sizeof(struct in_addr); + } + afd = find_afd(af); + if (afd == NULL) + continue; +#endif /* 0 */ + + GET_AI(cur->ai_next, afd, ap); + + /* GET_PORT(cur->ai_next, servname); */ + if ((pai->ai_flags & AI_CANONNAME) != 0) { + /* + * RFC2553 says that ai_canonname will be set only for + * the first element. we do it for all the elements, + * just for convenience. + */ + GET_CANONNAME(cur->ai_next, hp->h_name); + } + while (cur->ai_next) /*%< no need to loop, actually. */ + cur = cur->ai_next; + continue; + + free: + if (cur->ai_next) + freeaddrinfo(cur->ai_next); + cur->ai_next = NULL; + /* continue, without tht pointer CUR advanced. */ + } + + return(sentinel.ai_next); +} + +struct addrinfo * +addr2addrinfo(pai, cp) + const struct addrinfo *pai; + const char *cp; +{ + const struct afd *afd; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return(NULL); + + return(get_ai(pai, afd, cp)); +} + +static struct net_data * +init() +{ + struct net_data *net_data; + + if (!(net_data = net_data_init(NULL))) + goto error; + if (!net_data->ho) { + net_data->ho = (*net_data->irs->ho_map)(net_data->irs); + if (!net_data->ho || !net_data->res) { +error: + errno = EIO; + if (net_data && net_data->res) + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + + (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL); + } + + return (net_data); +} diff --git a/usr/src/lib/libresolv2_joy/common/irs/gethostent.c b/usr/src/lib/libresolv2_joy/common/irs/gethostent.c new file mode 100644 index 0000000000..77c3e0a890 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gethostent.c @@ -0,0 +1,1092 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#if !defined(__BIND_NOSTATIC) + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "irs_data.h" + +/* Definitions */ + +struct pvt { + char * aliases[1]; + char * addrs[2]; + char addr[NS_IN6ADDRSZ]; + char name[NS_MAXDNAME + 1]; + struct hostent host; +}; + +/* Forward */ + +static struct net_data *init(void); +static void freepvt(struct net_data *); +static struct hostent *fakeaddr(const char *, int, struct net_data *); + +#ifdef SUNW_OVERRIDE_RETRY +extern int __res_retry(int); +extern int __res_retry_reset(void); +#endif /* SUNW_OVERRIDE_RETRY */ + + +/* Public */ + +struct hostent * +gethostbyname(const char *name) { + struct net_data *net_data = init(); + + return (gethostbyname_p(name, net_data)); +} + +struct hostent * +gethostbyname2(const char *name, int af) { + struct net_data *net_data = init(); + + return (gethostbyname2_p(name, af, net_data)); +} + +struct hostent * +gethostbyaddr(const char *addr, int len, int af) { + struct net_data *net_data = init(); + + return (gethostbyaddr_p(addr, len, af, net_data)); +} + +struct hostent * +gethostent() { + struct net_data *net_data = init(); + + return (gethostent_p(net_data)); +} + +void +sethostent(int stayopen) { + struct net_data *net_data = init(); + sethostent_p(stayopen, net_data); +} + + +void +endhostent() { + struct net_data *net_data = init(); + endhostent_p(net_data); +} + +/* Shared private. */ + +struct hostent * +gethostbyname_p(const char *name, struct net_data *net_data) { + struct hostent *hp; + + if (!net_data) + return (NULL); + + if (net_data->res->options & RES_USE_INET6) { + hp = gethostbyname2_p(name, AF_INET6, net_data); + if (hp) + return (hp); + } + return (gethostbyname2_p(name, AF_INET, net_data)); +} + +struct hostent * +gethostbyname2_p(const char *name, int af, struct net_data *net_data) { + struct irs_ho *ho; + char tmp[NS_MAXDNAME]; + struct hostent *hp; + const char *cp; + char **hap; + + if (!net_data || !(ho = net_data->ho)) + return (NULL); + if (net_data->ho_stayopen && net_data->ho_last && + net_data->ho_last->h_addrtype == af) { + if (ns_samename(name, net_data->ho_last->h_name) == 1) + return (net_data->ho_last); + for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++) + if (ns_samename(name, *hap) == 1) + return (net_data->ho_last); + } + if (!strchr(name, '.') && (cp = res_hostalias(net_data->res, name, + tmp, sizeof tmp))) + name = cp; + if ((hp = fakeaddr(name, af, net_data)) != NULL) + return (hp); +#ifdef SUNW_OVERRIDE_RETRY + net_data->res->retry = __res_retry(net_data->res->retry); +#endif /* SUNW_OVERRIDE_RETRY */ + net_data->ho_last = (*ho->byname2)(ho, name, af); +#ifdef SUNW_OVERRIDE_RETRY + net_data->res->retry = __res_retry_reset(); +#endif /* SUNW_OVERRIDE_RETRY */ + if (!net_data->ho_stayopen) + endhostent(); + return (net_data->ho_last); +} + +struct hostent * +gethostbyaddr_p(const char *addr, int len, int af, struct net_data *net_data) { + struct irs_ho *ho; + char **hap; + + if (!net_data || !(ho = net_data->ho)) + return (NULL); + if (net_data->ho_stayopen && net_data->ho_last && + net_data->ho_last->h_length == len) + for (hap = net_data->ho_last->h_addr_list; + hap && *hap; + hap++) + if (!memcmp(addr, *hap, len)) + return (net_data->ho_last); + net_data->ho_last = (*ho->byaddr)(ho, addr, len, af); + if (!net_data->ho_stayopen) + endhostent(); + return (net_data->ho_last); +} + + +struct hostent * +gethostent_p(struct net_data *net_data) { + struct irs_ho *ho; + struct hostent *hp; + + if (!net_data || !(ho = net_data->ho)) + return (NULL); + while ((hp = (*ho->next)(ho)) != NULL && + hp->h_addrtype == AF_INET6 && + (net_data->res->options & RES_USE_INET6) == 0U) + continue; + net_data->ho_last = hp; + return (net_data->ho_last); +} + + +void +sethostent_p(int stayopen, struct net_data *net_data) { + struct irs_ho *ho; + + if (!net_data || !(ho = net_data->ho)) + return; + freepvt(net_data); + (*ho->rewind)(ho); + net_data->ho_stayopen = (stayopen != 0); + if (stayopen == 0) + net_data_minimize(net_data); +} + +void +endhostent_p(struct net_data *net_data) { + struct irs_ho *ho; + + if ((net_data != NULL) && ((ho = net_data->ho) != NULL)) + (*ho->minimize)(ho); +} + +#ifndef IN6_IS_ADDR_V4COMPAT +static const unsigned char in6addr_compat[12] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \ + ((x)->s6_addr[12] != 0 || \ + (x)->s6_addr[13] != 0 || \ + (x)->s6_addr[14] != 0 || \ + ((x)->s6_addr[15] != 0 && \ + (x)->s6_addr[15] != 1))) +#endif +#ifndef IN6_IS_ADDR_V4MAPPED +#define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12)) +#endif + +static const unsigned char in6addr_mapped[12] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + +static int scan_interfaces(int *, int *); +static struct hostent *copyandmerge(struct hostent *, struct hostent *, int, int *); + +/*% + * Public functions + */ + +/*% + * AI_V4MAPPED + AF_INET6 + * If no IPv6 address then a query for IPv4 and map returned values. + * + * AI_ALL + AI_V4MAPPED + AF_INET6 + * Return IPv6 and IPv4 mapped. + * + * AI_ADDRCONFIG + * Only return IPv6 / IPv4 address if there is an interface of that + * type active. + */ + +struct hostent * +getipnodebyname(const char *name, int af, int flags, int *error_num) { + int have_v4 = 1, have_v6 = 1; + struct in_addr in4; + struct in6_addr in6; + struct hostent he, *he1 = NULL, *he2 = NULL, *he3; + int v4 = 0, v6 = 0; + struct net_data *net_data = init(); + u_long options; + int tmp_err; + + if (net_data == NULL) { + *error_num = NO_RECOVERY; + return (NULL); + } + + /* If we care about active interfaces then check. */ + if ((flags & AI_ADDRCONFIG) != 0) + if (scan_interfaces(&have_v4, &have_v6) == -1) { + *error_num = NO_RECOVERY; + return (NULL); + } + + /* Check for literal address. */ + if ((v4 = inet_pton(AF_INET, name, &in4)) != 1) + v6 = inet_pton(AF_INET6, name, &in6); + + /* Impossible combination? */ + + if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) || + (af == AF_INET && v6 == 1) || + (have_v4 == 0 && v4 == 1) || + (have_v6 == 0 && v6 == 1) || + (have_v4 == 0 && af == AF_INET) || + (have_v6 == 0 && af == AF_INET6)) { + *error_num = HOST_NOT_FOUND; + return (NULL); + } + + /* Literal address? */ + if (v4 == 1 || v6 == 1) { + char *addr_list[2]; + char *aliases[1]; + + DE_CONST(name, he.h_name); + he.h_addr_list = addr_list; + he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6; + he.h_addr_list[1] = NULL; + he.h_aliases = aliases; + he.h_aliases[0] = NULL; + he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ; + he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6; + return (copyandmerge(&he, NULL, af, error_num)); + } + + options = net_data->res->options; + net_data->res->options &= ~RES_USE_INET6; + + tmp_err = NO_RECOVERY; + if (have_v6 && af == AF_INET6) { + he2 = gethostbyname2_p(name, AF_INET6, net_data); + if (he2 != NULL) { + he1 = copyandmerge(he2, NULL, af, error_num); + if (he1 == NULL) + return (NULL); + he2 = NULL; + } else { + tmp_err = net_data->res->res_h_errno; + } + } + + if (have_v4 && + ((af == AF_INET) || + (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 && + (he1 == NULL || (flags & AI_ALL) != 0)))) { + he2 = gethostbyname2_p(name, AF_INET, net_data); + if (he1 == NULL && he2 == NULL) { + *error_num = net_data->res->res_h_errno; + return (NULL); + } + } else + *error_num = tmp_err; + + net_data->res->options = options; + + he3 = copyandmerge(he1, he2, af, error_num); + + if (he1 != NULL) + freehostent(he1); + return (he3); +} + +struct hostent * +getipnodebyaddr(const void *src, size_t len, int af, int *error_num) { + struct hostent *he1, *he2; + struct net_data *net_data = init(); + + /* Sanity Checks. */ +#ifdef ORIGINAL_ISC_CODE + if (src == NULL) { +#else + /* this change was added circa May 2009, but not in ISC libbind 6.0 */ + if (src == NULL|| net_data == NULL) { +#endif /* ORIGINAL_ISC_CODE */ + *error_num = NO_RECOVERY; + return (NULL); + } + + switch (af) { + case AF_INET: + if (len != (size_t)INADDRSZ) { + *error_num = NO_RECOVERY; + return (NULL); + } + break; + case AF_INET6: + if (len != (size_t)IN6ADDRSZ) { + *error_num = NO_RECOVERY; + return (NULL); + } + break; + default: + *error_num = NO_RECOVERY; + return (NULL); + } + + /* + * Lookup IPv4 and IPv4 mapped/compatible addresses + */ + if ((af == AF_INET6 && + IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src)) || + (af == AF_INET6 && + IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src)) || + (af == AF_INET)) { + const char *cp = src; + + if (af == AF_INET6) + cp += 12; + he1 = gethostbyaddr_p(cp, 4, AF_INET, net_data); + if (he1 == NULL) { + *error_num = net_data->res->res_h_errno; + return (NULL); + } + he2 = copyandmerge(he1, NULL, af, error_num); + if (he2 == NULL) + return (NULL); + /* + * Restore original address if mapped/compatible. + */ + if (af == AF_INET6) + memcpy(he1->h_addr, src, len); + return (he2); + } + + /* + * Lookup IPv6 address. + */ + if (memcmp((const struct in6_addr *)src, &in6addr_any, 16) == 0) { + *error_num = HOST_NOT_FOUND; + return (NULL); + } + + he1 = gethostbyaddr_p(src, 16, AF_INET6, net_data); + if (he1 == NULL) { + *error_num = net_data->res->res_h_errno; + return (NULL); + } + return (copyandmerge(he1, NULL, af, error_num)); +} + +void +freehostent(struct hostent *he) { + char **cpp; + int names = 1; + int addresses = 1; + + memput(he->h_name, strlen(he->h_name) + 1); + + cpp = he->h_addr_list; + while (*cpp != NULL) { + memput(*cpp, (he->h_addrtype == AF_INET) ? + INADDRSZ : IN6ADDRSZ); + *cpp = NULL; + cpp++; + addresses++; + } + + cpp = he->h_aliases; + while (*cpp != NULL) { + memput(*cpp, strlen(*cpp) + 1); + cpp++; + names++; + } + + memput(he->h_aliases, sizeof(char *) * (names)); + memput(he->h_addr_list, sizeof(char *) * (addresses)); + memput(he, sizeof *he); +} + +/*% + * Private + */ + +/*% + * Scan the interface table and set have_v4 and have_v6 depending + * upon whether there are IPv4 and IPv6 interface addresses. + * + * Returns: + * 0 on success + * -1 on failure. + */ + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \ + !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF) + +#ifdef __hpux +#define lifc_len iflc_len +#define lifc_buf iflc_buf +#define lifc_req iflc_req +#define LIFCONF if_laddrconf +#else +#define SETFAMILYFLAGS +#define LIFCONF lifconf +#endif + +#ifdef __hpux +#define lifr_addr iflr_addr +#define lifr_name iflr_name +#define lifr_dstaddr iflr_dstaddr +#define lifr_flags iflr_flags +#define ss_family sa_family +#define LIFREQ if_laddrreq +#else +#define LIFREQ lifreq +#endif + +static void +scan_interfaces6(int *have_v4, int *have_v6) { + struct LIFCONF lifc; + struct LIFREQ lifreq; + struct in_addr in4; + struct in6_addr in6; + char *buf = NULL, *cp, *cplim; + static unsigned int bufsiz = 4095; + int s, cpsize, n; + + /* Get interface list from system. */ + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) + goto cleanup; + + /* + * Grow buffer until large enough to contain all interface + * descriptions. + */ + for (;;) { + buf = memget(bufsiz); + if (buf == NULL) + goto cleanup; +#ifdef SETFAMILYFLAGS + lifc.lifc_family = AF_UNSPEC; /*%< request all families */ + lifc.lifc_flags = 0; +#endif + lifc.lifc_len = bufsiz; + lifc.lifc_buf = buf; + if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) { + /* + * Some OS's just return what will fit rather + * than set EINVAL if the buffer is too small + * to fit all the interfaces in. If + * lifc.lifc_len is too near to the end of the + * buffer we will grow it just in case and + * retry. + */ + if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz) + break; + } + if ((n == -1) && errno != EINVAL) + goto cleanup; + + if (bufsiz > 1000000) + goto cleanup; + + memput(buf, bufsiz); + bufsiz += 4096; + } + + /* Parse system's interface list. */ + cplim = buf + lifc.lifc_len; /*%< skip over if's with big ifr_addr's */ + for (cp = buf; + (*have_v4 == 0 || *have_v6 == 0) && cp < cplim; + cp += cpsize) { + memcpy(&lifreq, cp, sizeof lifreq); +#ifdef HAVE_SA_LEN +#ifdef FIX_ZERO_SA_LEN + if (lifreq.lifr_addr.sa_len == 0) + lifreq.lifr_addr.sa_len = 16; +#endif +#ifdef HAVE_MINIMUM_IFREQ + cpsize = sizeof lifreq; + if (lifreq.lifr_addr.sa_len > sizeof (struct sockaddr)) + cpsize += (int)lifreq.lifr_addr.sa_len - + (int)(sizeof (struct sockaddr)); +#else + cpsize = sizeof lifreq.lifr_name + lifreq.lifr_addr.sa_len; +#endif /* HAVE_MINIMUM_IFREQ */ +#elif defined SIOCGIFCONF_ADDR + cpsize = sizeof lifreq; +#else + cpsize = sizeof lifreq.lifr_name; + /* XXX maybe this should be a hard error? */ + if (ioctl(s, SIOCGLIFADDR, (char *)&lifreq) < 0) + continue; +#endif + switch (lifreq.lifr_addr.ss_family) { + case AF_INET: + if (*have_v4 == 0) { + memcpy(&in4, + &((struct sockaddr_in *) + &lifreq.lifr_addr)->sin_addr, + sizeof in4); + if (in4.s_addr == INADDR_ANY) + break; + n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq); + if (n < 0) + break; + if ((lifreq.lifr_flags & IFF_UP) == 0) + break; + *have_v4 = 1; + } + break; + case AF_INET6: + if (*have_v6 == 0) { + memcpy(&in6, + &((struct sockaddr_in6 *) + &lifreq.lifr_addr)->sin6_addr, sizeof in6); + if (memcmp(&in6, &in6addr_any, sizeof in6) == 0) + break; + n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq); + if (n < 0) + break; + if ((lifreq.lifr_flags & IFF_UP) == 0) + break; + *have_v6 = 1; + } + break; + } + } + if (buf != NULL) + memput(buf, bufsiz); + close(s); + /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ + return; + cleanup: + if (buf != NULL) + memput(buf, bufsiz); + if (s != -1) + close(s); + /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ + return; +} +#endif + +#if ( defined(__linux__) || defined(__linux) || defined(LINUX) ) +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# else +# define IF_NAMESIZE 16 +# endif +#endif +static void +scan_linux6(int *have_v6) { + FILE *proc = NULL; + char address[33]; + char name[IF_NAMESIZE+1]; + int ifindex, prefix, flag3, flag4; + + proc = fopen("/proc/net/if_inet6", "r"); + if (proc == NULL) + return; + + if (fscanf(proc, "%32[a-f0-9] %x %x %x %x %16s\n", + address, &ifindex, &prefix, &flag3, &flag4, name) == 6) + *have_v6 = 1; + fclose(proc); + return; +} +#endif + +static int +scan_interfaces(int *have_v4, int *have_v6) { + struct ifconf ifc; + union { + char _pad[256]; /*%< leave space for IPv6 addresses */ + struct ifreq ifreq; + } u; + struct in_addr in4; + struct in6_addr in6; + char *buf = NULL, *cp, *cplim; + static unsigned int bufsiz = 4095; + int s, n; + size_t cpsize; + + /* Set to zero. Used as loop terminators below. */ + *have_v4 = *have_v6 = 0; + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \ + !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF) + /* + * Try to scan the interfaces using IPv6 ioctls(). + */ + scan_interfaces6(have_v4, have_v6); + if (*have_v4 != 0 && *have_v6 != 0) + return (0); +#endif +#ifdef __linux + scan_linux6(have_v6); +#endif + + /* Get interface list from system. */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + goto err_ret; + + /* + * Grow buffer until large enough to contain all interface + * descriptions. + */ + for (;;) { + buf = memget(bufsiz); + if (buf == NULL) + goto err_ret; + ifc.ifc_len = bufsiz; + ifc.ifc_buf = buf; +#ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF + /* + * This is a fix for IRIX OS in which the call to ioctl with + * the flag SIOCGIFCONF may not return an entry for all the + * interfaces like most flavors of Unix. + */ + if (emul_ioctl(&ifc) >= 0) + break; +#else + if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) { + /* + * Some OS's just return what will fit rather + * than set EINVAL if the buffer is too small + * to fit all the interfaces in. If + * ifc.ifc_len is too near to the end of the + * buffer we will grow it just in case and + * retry. + */ + if (ifc.ifc_len + 2 * sizeof(u.ifreq) < bufsiz) + break; + } +#endif + if ((n == -1) && errno != EINVAL) + goto err_ret; + + if (bufsiz > 1000000) + goto err_ret; + + memput(buf, bufsiz); + bufsiz += 4096; + } + + /* Parse system's interface list. */ + cplim = buf + ifc.ifc_len; /*%< skip over if's with big ifr_addr's */ + for (cp = buf; + (*have_v4 == 0 || *have_v6 == 0) && cp < cplim; + cp += cpsize) { + memcpy(&u.ifreq, cp, sizeof u.ifreq); +#ifdef HAVE_SA_LEN +#ifdef FIX_ZERO_SA_LEN + if (u.ifreq.ifr_addr.sa_len == 0) + u.ifreq.ifr_addr.sa_len = 16; +#endif +#ifdef HAVE_MINIMUM_IFREQ + cpsize = sizeof u.ifreq; + if (u.ifreq.ifr_addr.sa_len > sizeof (struct sockaddr)) + cpsize += (int)u.ifreq.ifr_addr.sa_len - + (int)(sizeof (struct sockaddr)); +#else + cpsize = sizeof u.ifreq.ifr_name + u.ifreq.ifr_addr.sa_len; +#endif /* HAVE_MINIMUM_IFREQ */ + if (cpsize > sizeof u.ifreq && cpsize <= sizeof u) + memcpy(&u.ifreq, cp, cpsize); +#elif defined SIOCGIFCONF_ADDR + cpsize = sizeof u.ifreq; +#else + cpsize = sizeof u.ifreq.ifr_name; + /* XXX maybe this should be a hard error? */ + if (ioctl(s, SIOCGIFADDR, (char *)&u.ifreq) < 0) + continue; +#endif + switch (u.ifreq.ifr_addr.sa_family) { + case AF_INET: + if (*have_v4 == 0) { + memcpy(&in4, + &((struct sockaddr_in *) + &u.ifreq.ifr_addr)->sin_addr, + sizeof in4); + if (in4.s_addr == INADDR_ANY) + break; + n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq); + if (n < 0) + break; + if ((u.ifreq.ifr_flags & IFF_UP) == 0) + break; + *have_v4 = 1; + } + break; + case AF_INET6: + if (*have_v6 == 0) { + memcpy(&in6, + &((struct sockaddr_in6 *) + &u.ifreq.ifr_addr)->sin6_addr, + sizeof in6); + if (memcmp(&in6, &in6addr_any, sizeof in6) == 0) + break; + n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq); + if (n < 0) + break; + if ((u.ifreq.ifr_flags & IFF_UP) == 0) + break; + *have_v6 = 1; + } + break; + } + } + if (buf != NULL) + memput(buf, bufsiz); + close(s); + /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ + return (0); + err_ret: + if (buf != NULL) + memput(buf, bufsiz); + if (s != -1) + close(s); + /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */ + return (-1); +} + +static struct hostent * +copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num) { + struct hostent *he = NULL; + int addresses = 1; /*%< NULL terminator */ + int names = 1; /*%< NULL terminator */ + int len = 0; + char **cpp, **npp; + + /* + * Work out array sizes; + */ + if (he1 != NULL) { + cpp = he1->h_addr_list; + while (*cpp != NULL) { + addresses++; + cpp++; + } + cpp = he1->h_aliases; + while (*cpp != NULL) { + names++; + cpp++; + } + } + + if (he2 != NULL) { + cpp = he2->h_addr_list; + while (*cpp != NULL) { + addresses++; + cpp++; + } + if (he1 == NULL) { + cpp = he2->h_aliases; + while (*cpp != NULL) { + names++; + cpp++; + } + } + } + + if (addresses == 1) { + *error_num = NO_ADDRESS; + return (NULL); + } + + he = memget(sizeof *he); + if (he == NULL) + goto no_recovery; + + he->h_addr_list = memget(sizeof(char *) * (addresses)); + if (he->h_addr_list == NULL) + goto cleanup0; + memset(he->h_addr_list, 0, sizeof(char *) * (addresses)); + + /* copy addresses */ + npp = he->h_addr_list; + if (he1 != NULL) { + cpp = he1->h_addr_list; + while (*cpp != NULL) { + *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + if (*npp == NULL) + goto cleanup1; + /* convert to mapped if required */ + if (af == AF_INET6 && he1->h_addrtype == AF_INET) { + memcpy(*npp, in6addr_mapped, + sizeof in6addr_mapped); + memcpy(*npp + sizeof in6addr_mapped, *cpp, + INADDRSZ); + } else { + memcpy(*npp, *cpp, + (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + } + cpp++; + npp++; + } + } + + if (he2 != NULL) { + cpp = he2->h_addr_list; + while (*cpp != NULL) { + *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + if (*npp == NULL) + goto cleanup1; + /* convert to mapped if required */ + if (af == AF_INET6 && he2->h_addrtype == AF_INET) { + memcpy(*npp, in6addr_mapped, + sizeof in6addr_mapped); + memcpy(*npp + sizeof in6addr_mapped, *cpp, + INADDRSZ); + } else { + memcpy(*npp, *cpp, + (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + } + cpp++; + npp++; + } + } + + he->h_aliases = memget(sizeof(char *) * (names)); + if (he->h_aliases == NULL) + goto cleanup1; + memset(he->h_aliases, 0, sizeof(char *) * (names)); + + /* copy aliases */ + npp = he->h_aliases; + cpp = (he1 != NULL) ? he1->h_aliases : he2->h_aliases; + while (*cpp != NULL) { + len = strlen (*cpp) + 1; + *npp = memget(len); + if (*npp == NULL) + goto cleanup2; + strcpy(*npp, *cpp); + npp++; + cpp++; + } + + /* copy hostname */ + he->h_name = memget(strlen((he1 != NULL) ? + he1->h_name : he2->h_name) + 1); + if (he->h_name == NULL) + goto cleanup2; + strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name); + + /* set address type and length */ + he->h_addrtype = af; + he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ; + return(he); + + cleanup2: + cpp = he->h_aliases; + while (*cpp != NULL) { + memput(*cpp, strlen(*cpp) + 1); + cpp++; + } + memput(he->h_aliases, sizeof(char *) * (names)); + + cleanup1: + cpp = he->h_addr_list; + while (*cpp != NULL) { + memput(*cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ); + *cpp = NULL; + cpp++; + } + memput(he->h_addr_list, sizeof(char *) * (addresses)); + + cleanup0: + memput(he, sizeof *he); + + no_recovery: + *error_num = NO_RECOVERY; + return (NULL); +} + +static struct net_data * +init() { + struct net_data *net_data; + + if (!(net_data = net_data_init(NULL))) + goto error; + if (!net_data->ho) { + net_data->ho = (*net_data->irs->ho_map)(net_data->irs); + if (!net_data->ho || !net_data->res) { + error: + errno = EIO; + +#ifdef SUNW_SETHERRNO + h_errno = NETDB_INTERNAL; +#endif /* SUNW_SETHERRNO */ + if (net_data && net_data->res) + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + + (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL); + } + + return (net_data); +} + +static void +freepvt(struct net_data *net_data) { + if (net_data->ho_data) { + free(net_data->ho_data); + net_data->ho_data = NULL; + } +} + +static struct hostent * +fakeaddr(const char *name, int af, struct net_data *net_data) { + struct pvt *pvt; + + freepvt(net_data); + net_data->ho_data = malloc(sizeof (struct pvt)); + if (!net_data->ho_data) { + errno = ENOMEM; + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + pvt = net_data->ho_data; +#ifndef __bsdi__ + /* + * Unlike its forebear(inet_aton), our friendly inet_pton() is strict + * in its interpretation of its input, and it will only return "1" if + * the input string is a formally valid(and thus unambiguous with + * respect to host names) internet address specification for this AF. + * + * This means "telnet 0xdeadbeef" and "telnet 127.1" are dead now. + */ + if (inet_pton(af, name, pvt->addr) != 1) { +#else + /* BSDI XXX + * We put this back to inet_aton -- we really want the old behavior + * Long live 127.1... + */ + if ((af != AF_INET || + inet_aton(name, (struct in_addr *)pvt->addr) != 1) && + inet_pton(af, name, pvt->addr) != 1) { +#endif + RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); + return (NULL); + } + strncpy(pvt->name, name, NS_MAXDNAME); + pvt->name[NS_MAXDNAME] = '\0'; + if (af == AF_INET && (net_data->res->options & RES_USE_INET6) != 0U) { + map_v4v6_address(pvt->addr, pvt->addr); + af = AF_INET6; + } + pvt->host.h_addrtype = af; + switch(af) { + case AF_INET: + pvt->host.h_length = NS_INADDRSZ; + break; + case AF_INET6: + pvt->host.h_length = NS_IN6ADDRSZ; + break; + default: + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + pvt->host.h_name = pvt->name; + pvt->host.h_aliases = pvt->aliases; + pvt->aliases[0] = NULL; + pvt->addrs[0] = (char *)pvt->addr; + pvt->addrs[1] = NULL; + pvt->host.h_addr_list = pvt->addrs; + RES_SET_H_ERRNO(net_data->res, NETDB_SUCCESS); + return (&pvt->host); +} + +#ifdef grot /*%< for future use in gethostbyaddr(), for "SUNSECURITY" */ + struct hostent *rhp; + char **haddr; + u_long old_options; + char hname2[MAXDNAME+1]; + + if (af == AF_INET) { + /* + * turn off search as the name should be absolute, + * 'localhost' should be matched by defnames + */ + strncpy(hname2, hp->h_name, MAXDNAME); + hname2[MAXDNAME] = '\0'; + old_options = net_data->res->options; + net_data->res->options &= ~RES_DNSRCH; + net_data->res->options |= RES_DEFNAMES; + if (!(rhp = gethostbyname(hname2))) { + net_data->res->options = old_options; + RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); + return (NULL); + } + net_data->res->options = old_options; + for (haddr = rhp->h_addr_list; *haddr; haddr++) + if (!memcmp(*haddr, addr, INADDRSZ)) + break; + if (!*haddr) { + RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); + return (NULL); + } + } +#endif /* grot */ +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c b/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c new file mode 100644 index 0000000000..50e03f5773 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#if !defined(_REENTRANT) || !defined(DO_PTHREADS) + static int gethostent_r_not_required = 0; +#else +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/param.h> +#include <port_after.h> + +#pragma redefine extname res_gethostbyname joy_res_gethostbyname +#pragma redefine extname res_gethostbyaddr joy_res_gethostbyaddr + +#ifdef HOST_R_RETURN + +static HOST_R_RETURN +copy_hostent(struct hostent *, struct hostent *, HOST_R_COPY_ARGS); + +HOST_R_RETURN +gethostbyname_r(const char *name, struct hostent *hptr, HOST_R_ARGS) { + struct hostent *he = gethostbyname(name); +#ifdef HOST_R_SETANSWER + int n = 0; +#endif + +#ifdef HOST_R_ERRNO + HOST_R_ERRNO; +#endif + +#ifdef HOST_R_SETANSWER + if (he == NULL || (n = copy_hostent(he, hptr, HOST_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = hptr; + + return (n); +#else + if (he == NULL) + return (HOST_R_BAD); + + return (copy_hostent(he, hptr, HOST_R_COPY)); +#endif +} + +HOST_R_RETURN +gethostbyaddr_r(const char *addr, int len, int type, + struct hostent *hptr, HOST_R_ARGS) { + struct hostent *he = gethostbyaddr(addr, len, type); +#ifdef HOST_R_SETANSWER + int n = 0; +#endif + +#ifdef HOST_R_ERRNO + HOST_R_ERRNO; +#endif + +#ifdef HOST_R_SETANSWER + if (he == NULL || (n = copy_hostent(he, hptr, HOST_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = hptr; + + return (n); +#else + if (he == NULL) + return (HOST_R_BAD); + + return (copy_hostent(he, hptr, HOST_R_COPY)); +#endif +} + +/*% + * These assume a single context is in operation per thread. + * If this is not the case we will need to call irs directly + * rather than through the base functions. + */ + +HOST_R_RETURN +gethostent_r(struct hostent *hptr, HOST_R_ARGS) { + struct hostent *he = gethostent(); +#ifdef HOST_R_SETANSWER + int n = 0; +#endif + +#ifdef HOST_R_ERRNO + HOST_R_ERRNO; +#endif + +#ifdef HOST_R_SETANSWER + if (he == NULL || (n = copy_hostent(he, hptr, HOST_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = hptr; + + return (n); +#else + if (he == NULL) + return (HOST_R_BAD); + + return (copy_hostent(he, hptr, HOST_R_COPY)); +#endif +} + +HOST_R_SET_RETURN +#ifdef HOST_R_ENT_ARGS +sethostent_r(int stay_open, HOST_R_ENT_ARGS) +#else +sethostent_r(int stay_open) +#endif +{ +#ifdef HOST_R_ENT_ARGS + UNUSED(hdptr); +#endif + sethostent(stay_open); +#ifdef HOST_R_SET_RESULT + return (HOST_R_SET_RESULT); +#endif +} + +HOST_R_END_RETURN +#ifdef HOST_R_ENT_ARGS +endhostent_r(HOST_R_ENT_ARGS) +#else +endhostent_r(void) +#endif +{ +#ifdef HOST_R_ENT_ARGS + UNUSED(hdptr); +#endif + endhostent(); + HOST_R_END_RESULT(HOST_R_OK); +} + +/* Private */ + +#ifndef HOSTENT_DATA +static HOST_R_RETURN +copy_hostent(struct hostent *he, struct hostent *hptr, HOST_R_COPY_ARGS) { + 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 (HOST_R_BAD); + } + + /* 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 (HOST_R_OK); +} +#else /* !HOSTENT_DATA */ +static int +copy_hostent(struct hostent *he, struct hostent *hptr, HOST_R_COPY_ARGS) { + char *cp, *eob; + int i, n; + + /* copy address size and type */ + hptr->h_addrtype = he->h_addrtype; + n = hptr->h_length = he->h_length; + + /* copy up to first 35 addresses */ + i = 0; + cp = hdptr->hostbuf; + eob = hdptr->hostbuf + sizeof(hdptr->hostbuf); + hptr->h_addr_list = hdptr->h_addr_ptrs; + while (he->h_addr_list[i] && i < (_MAXADDRS)) { + if (n < (eob - cp)) { + memcpy(cp, he->h_addr_list[i], n); + hptr->h_addr_list[i] = cp; + cp += n; + } else { + break; + } + i++; + } + hptr->h_addr_list[i] = NULL; + + /* copy official name */ + if ((n = strlen(he->h_name) + 1) < (eob - cp)) { + strcpy(cp, he->h_name); + hptr->h_name = cp; + cp += n; + } else { + return (-1); + } + + /* copy aliases */ + i = 0; + hptr->h_aliases = hdptr->host_aliases; + while (he->h_aliases[i] && i < (_MAXALIASES-1)) { + if ((n = strlen(he->h_aliases[i]) + 1) < (eob - cp)) { + strcpy(cp, he->h_aliases[i]); + hptr->h_aliases[i] = cp; + cp += n; + } else { + break; + } + i++; + } + hptr->h_aliases[i] = NULL; + + return (HOST_R_OK); +} +#endif /* !HOSTENT_DATA */ +#else /* HOST_R_RETURN */ + static int gethostent_r_unknown_system = 0; +#endif /* HOST_R_RETURN */ +#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c b/usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c new file mode 100644 index 0000000000..360bda8bfe --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c @@ -0,0 +1,334 @@ +/* + * Issues to be discussed: + * - Thread safe-ness must be checked + */ + +#if ( defined(__linux__) || defined(__linux) || defined(LINUX) ) +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# else +# define IF_NAMESIZE 16 +# endif +#endif +#endif + +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by WIDE Project and + * its contributors. + * 4. 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. + */ + +#include <port_before.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <netdb.h> +#include <resolv_joy.h> +#include <string.h> +#include <stddef.h> + +#include <port_after.h> + +/*% + * Note that a_off will be dynamically adjusted so that to be consistent + * with the definition of sockaddr_in{,6}. + * The value presented below is just a guess. + */ +static struct afd { + int a_af; + int a_addrlen; + size_t a_socklen; + int a_off; +} afdl [] = { + /* first entry is linked last... */ + {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr)}, + {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr)}, + {0, 0, 0, 0}, +}; + +struct sockinet { +#ifdef HAVE_SA_LEN + u_char si_len; +#endif + u_char si_family; + u_short si_port; +}; + +static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *, + size_t, int)); +#ifdef HAVE_SIN6_SCOPE_ID +static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int)); +#endif + +int +getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) + const struct sockaddr *sa; + size_t salen; + char *host; + size_t hostlen; + char *serv; + size_t servlen; + int flags; +{ + struct afd *afd; + struct servent *sp; + struct hostent *hp; + u_short port; +#ifdef HAVE_SA_LEN + size_t len; +#endif + int family, i; + const char *addr; + char *p; + char numserv[512]; + char numaddr[512]; + const struct sockaddr_in6 *sin6; + + if (sa == NULL) + return EAI_FAIL; + +#ifdef HAVE_SA_LEN + len = sa->sa_len; + if (len != salen) return EAI_FAIL; +#endif + + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) + if (afdl[i].a_af == family) { + afd = &afdl[i]; + goto found; + } + return EAI_FAMILY; + + found: + if (salen != afd->a_socklen) return EAI_FAIL; + + port = ((const struct sockinet *)sa)->si_port; /*%< network byte order */ + addr = (const char *)sa + afd->a_off; + + if (serv == NULL || servlen == 0U) { + /* + * rfc2553bis says that serv == NULL or servlen == 0 means that + * the caller does not want the result. + */ + } else if (flags & NI_NUMERICSERV) { + sprintf(numserv, "%d", ntohs(port)); + if (strlen(numserv) > servlen) + return EAI_MEMORY; + strcpy(serv, numserv); + } else { + sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); + if (sp) { + if (strlen(sp->s_name) + 1 > servlen) + return EAI_MEMORY; + strcpy(serv, sp->s_name); + } else + return EAI_NONAME; + } + + switch (sa->sa_family) { + case AF_INET: + if (ntohl(*(const u_int32_t *)addr) >> IN_CLASSA_NSHIFT == 0) + flags |= NI_NUMERICHOST; + break; + case AF_INET6: + sin6 = (const struct sockaddr_in6 *)sa; + switch (sin6->sin6_addr.s6_addr[0]) { + case 0x00: + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + ; + else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + ; + else + flags |= NI_NUMERICHOST; + break; + default: + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; + else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; + break; + } + break; + } + if (host == NULL || hostlen == 0U) { + /* + * rfc2553bis says that host == NULL or hostlen == 0 means that + * the caller does not want the result. + */ + } else if (flags & NI_NUMERICHOST) { + goto numeric; + } else { + hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); + + if (hp) { + if (flags & NI_NOFQDN) { + p = strchr(hp->h_name, '.'); + if (p) *p = '\0'; + } + if (strlen(hp->h_name) + 1 > hostlen) + return EAI_MEMORY; + strcpy(host, hp->h_name); + } else { + if (flags & NI_NAMEREQD) + return EAI_NONAME; + numeric: + switch(afd->a_af) { + case AF_INET6: + { + int error; + + if ((error = ip6_parsenumeric(sa, addr, host, + hostlen, + flags)) != 0) + return(error); + break; + } + + default: + if (inet_ntop(afd->a_af, addr, numaddr, + sizeof(numaddr)) == NULL) + return EAI_NONAME; + if (strlen(numaddr) + 1 > hostlen) + return EAI_MEMORY; + strcpy(host, numaddr); + } + } + } + return(0); +} + +static int +ip6_parsenumeric(const struct sockaddr *sa, const char *addr, char *host, + size_t hostlen, int flags) +{ + size_t numaddrlen; + char numaddr[512]; + +#ifndef HAVE_SIN6_SCOPE_ID + UNUSED(sa); + UNUSED(flags); +#endif + + if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) + == NULL) + return EAI_SYSTEM; + + numaddrlen = strlen(numaddr); + if (numaddrlen + 1 > hostlen) /*%< don't forget terminator */ + return EAI_MEMORY; + strcpy(host, numaddr); + +#ifdef HAVE_SIN6_SCOPE_ID + if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) { + char scopebuf[MAXHOSTNAMELEN]; /*%< XXX */ + int scopelen; + + /* ip6_sa2str never fails */ + scopelen = ip6_sa2str((const struct sockaddr_in6 *)sa, + scopebuf, sizeof(scopebuf), flags); + + if (scopelen + 1 + numaddrlen + 1 > hostlen) + return EAI_MEMORY; + + /* construct <numeric-addr><delim><scopeid> */ + memcpy(host + numaddrlen + 1, scopebuf, + scopelen); + host[numaddrlen] = SCOPE_DELIMITER; + host[numaddrlen + 1 + scopelen] = '\0'; + } +#endif + + return 0; +} + +#ifdef HAVE_SIN6_SCOPE_ID +/* ARGSUSED */ +static int +ip6_sa2str(const struct sockaddr_in6 *sa6, char *buf, + size_t bufsiz, int flags) +{ +#ifdef USE_IFNAMELINKID + unsigned int ifindex = (unsigned int)sa6->sin6_scope_id; + const struct in6_addr *a6 = &sa6->sin6_addr; +#endif + char tmp[64]; + +#ifdef NI_NUMERICSCOPE + if (flags & NI_NUMERICSCOPE) { + sprintf(tmp, "%u", sa6->sin6_scope_id); + if (bufsiz != 0U) { + strncpy(buf, tmp, bufsiz - 1); + buf[bufsiz - 1] = '\0'; + } + return(strlen(tmp)); + } +#endif + +#ifdef USE_IFNAMELINKID + /* + * For a link-local address, convert the index to an interface + * name, assuming a one-to-one mapping between links and interfaces. + * Note, however, that this assumption is stronger than the + * specification of the scoped address architecture; the + * specficication says that more than one interfaces can belong to + * a single link. + */ + + /* if_indextoname() does not take buffer size. not a good api... */ + if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) && + bufsiz >= IF_NAMESIZE) { + char *p = if_indextoname(ifindex, buf); + if (p) { + return(strlen(p)); + } + } +#endif + + /* last resort */ + sprintf(tmp, "%u", sa6->sin6_scope_id); + if (bufsiz != 0U) { + strncpy(buf, tmp, bufsiz - 1); + buf[bufsiz - 1] = '\0'; + } + return(strlen(tmp)); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetent.c b/usr/src/lib/libresolv2_joy/common/irs/getnetent.c new file mode 100644 index 0000000000..b3ba37547b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetent.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#if !defined(__BIND_NOSTATIC) + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdlib.h> +#include <string.h> + +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "irs_data.h" + +/* Definitions */ + +struct pvt { + struct netent netent; + char * aliases[1]; + char name[MAXDNAME + 1]; +}; + +/* Forward */ + +static struct net_data *init(void); +static struct netent *nw_to_net(struct nwent *, struct net_data *); +static void freepvt(struct net_data *); +static struct netent *fakeaddr(const char *, int af, struct net_data *); + +/* Portability */ + +#ifndef INADDR_NONE +# define INADDR_NONE 0xffffffff +#endif + +/* Public */ + +struct netent * +getnetent() { + struct net_data *net_data = init(); + + return (getnetent_p(net_data)); +} + +struct netent * +getnetbyname(const char *name) { + struct net_data *net_data = init(); + + return (getnetbyname_p(name, net_data)); +} + +struct netent * +getnetbyaddr(unsigned long net, int type) { + struct net_data *net_data = init(); + + return (getnetbyaddr_p(net, type, net_data)); +} + +void +setnetent(int stayopen) { + struct net_data *net_data = init(); + + setnetent_p(stayopen, net_data); +} + + +void +endnetent() { + struct net_data *net_data = init(); + + endnetent_p(net_data); +} + +/* Shared private. */ + +struct netent * +getnetent_p(struct net_data *net_data) { + struct irs_nw *nw; + + if (!net_data || !(nw = net_data->nw)) + return (NULL); + net_data->nww_last = (*nw->next)(nw); + net_data->nw_last = nw_to_net(net_data->nww_last, net_data); + return (net_data->nw_last); +} + +struct netent * +getnetbyname_p(const char *name, struct net_data *net_data) { + struct irs_nw *nw; + struct netent *np; + char **nap; + + if (!net_data || !(nw = net_data->nw)) + return (NULL); + if (net_data->nw_stayopen && net_data->nw_last) { + if (!strcmp(net_data->nw_last->n_name, name)) + return (net_data->nw_last); + for (nap = net_data->nw_last->n_aliases; nap && *nap; nap++) + if (!strcmp(name, *nap)) + return (net_data->nw_last); + } + if ((np = fakeaddr(name, AF_INET, net_data)) != NULL) + return (np); + net_data->nww_last = (*nw->byname)(nw, name, AF_INET); + net_data->nw_last = nw_to_net(net_data->nww_last, net_data); + if (!net_data->nw_stayopen) + endnetent(); + return (net_data->nw_last); +} + +struct netent * +getnetbyaddr_p(unsigned long net, int type, struct net_data *net_data) { + struct irs_nw *nw; + u_char addr[4]; + int bits; + + if (!net_data || !(nw = net_data->nw)) + return (NULL); + if (net_data->nw_stayopen && net_data->nw_last) + if (type == net_data->nw_last->n_addrtype && + net == net_data->nw_last->n_net) + return (net_data->nw_last); + + /* cannonize net(host order) */ + if (net < 256UL) { + net <<= 24; + bits = 8; + } else if (net < 65536UL) { + net <<= 16; + bits = 16; + } else if (net < 16777216UL) { + net <<= 8; + bits = 24; + } else + bits = 32; + + /* convert to net order */ + addr[0] = (0xFF000000 & net) >> 24; + addr[1] = (0x00FF0000 & net) >> 16; + addr[2] = (0x0000FF00 & net) >> 8; + addr[3] = (0x000000FF & net); + + /* reduce bits to as close to natural number as possible */ + if ((bits == 32) && (addr[0] < 224) && (addr[3] == 0)) { + if ((addr[0] < 192) && (addr[2] == 0)) { + if ((addr[0] < 128) && (addr[1] == 0)) + bits = 8; + else + bits = 16; + } else { + bits = 24; + } + } + + net_data->nww_last = (*nw->byaddr)(nw, addr, bits, AF_INET); + net_data->nw_last = nw_to_net(net_data->nww_last, net_data); + if (!net_data->nw_stayopen) + endnetent(); + return (net_data->nw_last); +} + + + + +void +setnetent_p(int stayopen, struct net_data *net_data) { + struct irs_nw *nw; + + if (!net_data || !(nw = net_data->nw)) + return; + freepvt(net_data); + (*nw->rewind)(nw); + net_data->nw_stayopen = (stayopen != 0); + if (stayopen == 0) + net_data_minimize(net_data); +} + +void +endnetent_p(struct net_data *net_data) { + struct irs_nw *nw; + + if ((net_data != NULL) && ((nw = net_data->nw) != NULL)) + (*nw->minimize)(nw); +} + +/* Private */ + +static struct net_data * +init() { + struct net_data *net_data; + + if (!(net_data = net_data_init(NULL))) + goto error; + if (!net_data->nw) { + net_data->nw = (*net_data->irs->nw_map)(net_data->irs); + + if (!net_data->nw || !net_data->res) { + error: + errno = EIO; + return (NULL); + } + (*net_data->nw->res_set)(net_data->nw, net_data->res, NULL); + } + + return (net_data); +} + +static void +freepvt(struct net_data *net_data) { + if (net_data->nw_data) { + free(net_data->nw_data); + net_data->nw_data = NULL; + } +} + +static struct netent * +fakeaddr(const char *name, int af, struct net_data *net_data) { + struct pvt *pvt; + const char *cp; + u_long tmp; + + if (af != AF_INET) { + /* XXX should support IPv6 some day */ + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + if (!isascii((unsigned char)(name[0])) || + !isdigit((unsigned char)(name[0]))) + return (NULL); + for (cp = name; *cp; ++cp) + if (!isascii(*cp) || (!isdigit((unsigned char)*cp) && *cp != '.')) + return (NULL); + if (*--cp == '.') + return (NULL); + + /* All-numeric, no dot at the end. */ + + tmp = inet_network(name); + if (tmp == INADDR_NONE) { + RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND); + return (NULL); + } + + /* Valid network number specified. + * Fake up a netent as if we'd actually + * done a lookup. + */ + freepvt(net_data); + net_data->nw_data = malloc(sizeof (struct pvt)); + if (!net_data->nw_data) { + errno = ENOMEM; + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + pvt = net_data->nw_data; + + strncpy(pvt->name, name, MAXDNAME); + pvt->name[MAXDNAME] = '\0'; + pvt->netent.n_name = pvt->name; + pvt->netent.n_addrtype = AF_INET; + pvt->netent.n_aliases = pvt->aliases; + pvt->aliases[0] = NULL; + pvt->netent.n_net = tmp; + + return (&pvt->netent); +} + +static struct netent * +nw_to_net(struct nwent *nwent, struct net_data *net_data) { + struct pvt *pvt; + u_long addr = 0; + int i; + int msbyte; + + if (!nwent || nwent->n_addrtype != AF_INET) + return (NULL); + freepvt(net_data); + net_data->nw_data = malloc(sizeof (struct pvt)); + if (!net_data->nw_data) { + errno = ENOMEM; + RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL); + return (NULL); + } + pvt = net_data->nw_data; + pvt->netent.n_name = nwent->n_name; + pvt->netent.n_aliases = nwent->n_aliases; + pvt->netent.n_addrtype = nwent->n_addrtype; + +/*% + * What this code does: Converts net addresses from network to host form. + * + * msbyte: the index of the most significant byte in the n_addr array. + * + * Shift bytes in significant order into addr. When all signicant + * bytes are in, zero out bits in the LSB that are not part of the network. + */ + msbyte = nwent->n_length / 8 + + ((nwent->n_length % 8) != 0 ? 1 : 0) - 1; + for (i = 0; i <= msbyte; i++) + addr = (addr << 8) | ((unsigned char *)nwent->n_addr)[i]; + i = (32 - nwent->n_length) % 8; + if (i != 0) + addr &= ~((1 << (i + 1)) - 1); + pvt->netent.n_net = addr; + return (&pvt->netent); +} + +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c new file mode 100644 index 0000000000..df6831ff12 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#if !defined(_REENTRANT) || !defined(DO_PTHREADS) + static int getnetent_r_not_required = 0; +#else +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/param.h> +#include <port_after.h> + +#ifdef NET_R_RETURN + +static NET_R_RETURN +copy_netent(struct netent *, struct netent *, NET_R_COPY_ARGS); + +NET_R_RETURN +getnetbyname_r(const char *name, struct netent *nptr, NET_R_ARGS) { + struct netent *ne = getnetbyname(name); +#ifdef NET_R_SETANSWER + int n = 0; + + if (ne == NULL || (n = copy_netent(ne, nptr, NET_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = ne; + if (ne == NULL) + *h_errnop = h_errno; + return (n); +#else + if (ne == NULL) + return (NET_R_BAD); + + return (copy_netent(ne, nptr, NET_R_COPY)); +#endif +} + +#ifndef GETNETBYADDR_ADDR_T +#define GETNETBYADDR_ADDR_T long +#endif +NET_R_RETURN +getnetbyaddr_r(GETNETBYADDR_ADDR_T addr, int type, struct netent *nptr, NET_R_ARGS) { + struct netent *ne = getnetbyaddr(addr, type); +#ifdef NET_R_SETANSWER + int n = 0; + + if (ne == NULL || (n = copy_netent(ne, nptr, NET_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = ne; + if (ne == NULL) + *h_errnop = h_errno; + return (n); +#else + + if (ne == NULL) + return (NET_R_BAD); + + return (copy_netent(ne, nptr, NET_R_COPY)); +#endif +} + +/*% + * These assume a single context is in operation per thread. + * If this is not the case we will need to call irs directly + * rather than through the base functions. + */ + +NET_R_RETURN +getnetent_r(struct netent *nptr, NET_R_ARGS) { + struct netent *ne = getnetent(); +#ifdef NET_R_SETANSWER + int n = 0; + + if (ne == NULL || (n = copy_netent(ne, nptr, NET_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = ne; + if (ne == NULL) + *h_errnop = h_errno; + return (n); +#else + + if (ne == NULL) + return (NET_R_BAD); + + return (copy_netent(ne, nptr, NET_R_COPY)); +#endif +} + +NET_R_SET_RETURN +#ifdef NET_R_ENT_ARGS +setnetent_r(int stay_open, NET_R_ENT_ARGS) +#else +setnetent_r(int stay_open) +#endif +{ +#ifdef NET_R_ENT_ARGS + UNUSED(ndptr); +#endif + setnetent(stay_open); +#ifdef NET_R_SET_RESULT + return (NET_R_SET_RESULT); +#endif +} + +NET_R_END_RETURN +#ifdef NET_R_ENT_ARGS +endnetent_r(NET_R_ENT_ARGS) +#else +endnetent_r() +#endif +{ +#ifdef NET_R_ENT_ARGS + UNUSED(ndptr); +#endif + endnetent(); + NET_R_END_RESULT(NET_R_OK); +} + +/* Private */ + +#ifndef NETENT_DATA +static NET_R_RETURN +copy_netent(struct netent *ne, struct netent *nptr, NET_R_COPY_ARGS) { + 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 (NET_R_BAD); + } + + /* 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 (NET_R_OK); +} +#else /* !NETENT_DATA */ +static int +copy_netent(struct netent *ne, struct netent *nptr, NET_R_COPY_ARGS) { + char *cp, *eob; + int i, n; + + /* copy net value and type */ + nptr->n_addrtype = ne->n_addrtype; + nptr->n_net = ne->n_net; + + /* copy official name */ + cp = ndptr->line; + eob = ndptr->line + sizeof(ndptr->line); + if ((n = strlen(ne->n_name) + 1) < (eob - cp)) { + strcpy(cp, ne->n_name); + nptr->n_name = cp; + cp += n; + } else { + return (-1); + } + + /* copy aliases */ + i = 0; + nptr->n_aliases = ndptr->net_aliases; + while (ne->n_aliases[i] && i < (_MAXALIASES-1)) { + if ((n = strlen(ne->n_aliases[i]) + 1) < (eob - cp)) { + strcpy(cp, ne->n_aliases[i]); + nptr->n_aliases[i] = cp; + cp += n; + } else { + break; + } + i++; + } + nptr->n_aliases[i] = NULL; + + return (NET_R_OK); +} +#endif /* !NETENT_DATA */ +#else /* NET_R_RETURN */ + static int getnetent_r_unknown_system = 0; +#endif /* NET_R_RETURN */ +#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c new file mode 100644 index 0000000000..dbcd1da486 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1996-1999, 2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#if !defined(__BIND_NOSTATIC) + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <stdio.h> + +#include <irs.h> + +#include "port_after.h" + +#include "irs_data.h" + +/* Forward */ + +static struct net_data *init(void); + + +/* Public */ + +#ifndef SETNETGRENT_ARGS +#define SETNETGRENT_ARGS const char *netgroup +#endif +void +setnetgrent(SETNETGRENT_ARGS) { + struct net_data *net_data = init(); + + setnetgrent_p(netgroup, net_data); +} + +void +endnetgrent(void) { + struct net_data *net_data = init(); + + endnetgrent_p(net_data); +} + +#ifndef INNETGR_ARGS +#define INNETGR_ARGS const char *netgroup, const char *host, \ + const char *user, const char *domain +#endif +int +innetgr(INNETGR_ARGS) { + struct net_data *net_data = init(); + + return (innetgr_p(netgroup, host, user, domain, net_data)); +} + +int +getnetgrent(NGR_R_CONST char **host, NGR_R_CONST char **user, + NGR_R_CONST char **domain) +{ + struct net_data *net_data = init(); + const char *ch, *cu, *cd; + int ret; + + ret = getnetgrent_p(&ch, &cu, &cd, net_data); + if (ret != 1) + return (ret); + + DE_CONST(ch, *host); + DE_CONST(cu, *user); + DE_CONST(cd, *domain); + return (ret); +} + +/* Shared private. */ + +void +setnetgrent_p(const char *netgroup, struct net_data *net_data) { + struct irs_ng *ng; + + if ((net_data != NULL) && ((ng = net_data->ng) != NULL)) + (*ng->rewind)(ng, netgroup); +} + +void +endnetgrent_p(struct net_data *net_data) { + struct irs_ng *ng; + + if (!net_data) + return; + if ((ng = net_data->ng) != NULL) + (*ng->close)(ng); + net_data->ng = NULL; +} + +int +innetgr_p(const char *netgroup, const char *host, + const char *user, const char *domain, + struct net_data *net_data) { + struct irs_ng *ng; + + if (!net_data || !(ng = net_data->ng)) + return (0); + return ((*ng->test)(ng, netgroup, host, user, domain)); +} + +int +getnetgrent_p(const char **host, const char **user, const char **domain, + struct net_data *net_data ) { + struct irs_ng *ng; + + if (!net_data || !(ng = net_data->ng)) + return (0); + return ((*ng->next)(ng, host, user, domain)); +} + +/* Private */ + +static struct net_data * +init(void) { + struct net_data *net_data; + + if (!(net_data = net_data_init(NULL))) + goto error; + if (!net_data->ng) { + net_data->ng = (*net_data->irs->ng_map)(net_data->irs); + if (!net_data->ng) { + error: + errno = EIO; + return (NULL); + } + } + + return (net_data); +} + +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c new file mode 100644 index 0000000000..2cc697910e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1998, 1999, 2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#if !defined(_REENTRANT) || !defined(DO_PTHREADS) + static int getnetgrent_r_not_required = 0; +#else +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <stdlib.h> +#include <port_after.h> + +#ifdef NGR_R_RETURN +#ifndef NGR_R_PRIVATE +#define NGR_R_PRIVATE 0 +#endif + +static NGR_R_RETURN +copy_protoent(NGR_R_CONST char **, NGR_R_CONST char **, NGR_R_CONST char **, + const char *, const char *, const char *, NGR_R_COPY_ARGS); + +NGR_R_RETURN +innetgr_r(const char *netgroup, const char *host, const char *user, + const char *domain) { + char *ng, *ho, *us, *dom; + + DE_CONST(netgroup, ng); + DE_CONST(host, ho); + DE_CONST(user, us); + DE_CONST(domain, dom); + + return (innetgr(ng, ho, us, dom)); +} + +/*% + * These assume a single context is in operation per thread. + * If this is not the case we will need to call irs directly + * rather than through the base functions. + */ + +NGR_R_RETURN +getnetgrent_r(NGR_R_CONST char **machinep, NGR_R_CONST char **userp, + NGR_R_CONST char **domainp, NGR_R_ARGS) +{ + NGR_R_CONST char *mp, *up, *dp; + int res = getnetgrent(&mp, &up, &dp); + + if (res != 1) + return (res); + + return (copy_protoent(machinep, userp, domainp, + mp, up, dp, NGR_R_COPY)); +} + +#if NGR_R_PRIVATE == 2 +struct private { + char *buf; +}; + +#endif +NGR_R_SET_RETURN +#ifdef NGR_R_SET_ARGS +setnetgrent_r(NGR_R_SET_CONST char *netgroup, NGR_R_SET_ARGS) +#else +setnetgrent_r(NGR_R_SET_CONST char *netgroup) +#endif +{ +#if NGR_R_PRIVATE == 2 + struct private *p; +#endif + char *tmp; +#if defined(NGR_R_SET_ARGS) && NGR_R_PRIVATE == 0 + UNUSED(buf); + UNUSED(buflen); +#endif + + DE_CONST(netgroup, tmp); + setnetgrent(tmp); + +#if NGR_R_PRIVATE == 1 + *buf = NULL; +#elif NGR_R_PRIVATE == 2 + *buf = p = malloc(sizeof(struct private)); + if (p == NULL) +#ifdef NGR_R_SET_RESULT + return (NGR_R_BAD); +#else + return; +#endif + p->buf = NULL; +#endif +#ifdef NGR_R_SET_RESULT + return (NGR_R_SET_RESULT); +#endif +} + +NGR_R_END_RETURN +#ifdef NGR_R_END_ARGS +endnetgrent_r(NGR_R_END_ARGS) +#else +endnetgrent_r(void) +#endif +{ +#if NGR_R_PRIVATE == 2 + struct private *p = buf; +#endif +#if defined(NGR_R_SET_ARGS) && NGR_R_PRIVATE == 0 + UNUSED(buf); + UNUSED(buflen); +#endif + + endnetgrent(); +#if NGR_R_PRIVATE == 1 + if (*buf != NULL) + free(*buf); + *buf = NULL; +#elif NGR_R_PRIVATE == 2 + if (p->buf != NULL) + free(p->buf); + free(p); +#endif + NGR_R_END_RESULT(NGR_R_OK); +} + +/* Private */ + +static int +copy_protoent(NGR_R_CONST char **machinep, NGR_R_CONST char **userp, + NGR_R_CONST char **domainp, const char *mp, const char *up, + const char *dp, NGR_R_COPY_ARGS) +{ +#if NGR_R_PRIVATE == 2 + struct private *p = buf; +#endif + char *cp; + int n; + int len; + + /* Find out the amount of space required to store the answer. */ + len = 0; + if (mp != NULL) len += strlen(mp) + 1; + if (up != NULL) len += strlen(up) + 1; + if (dp != NULL) len += strlen(dp) + 1; + +#if NGR_R_PRIVATE == 1 + if (*buf != NULL) + free(*buf); + *buf = malloc(len); + if (*buf == NULL) + return(NGR_R_BAD); + cp = *buf; +#elif NGR_R_PRIVATE == 2 + if (p->buf) + free(p->buf); + p->buf = malloc(len); + if (p->buf == NULL) + return(NGR_R_BAD); + cp = p->buf; +#else + if (len > (int)buflen) { + errno = ERANGE; + return (NGR_R_BAD); + } + cp = buf; +#endif + + if (mp != NULL) { + n = strlen(mp) + 1; + strcpy(cp, mp); + *machinep = cp; + cp += n; + } else + *machinep = NULL; + + if (up != NULL) { + n = strlen(up) + 1; + strcpy(cp, up); + *userp = cp; + cp += n; + } else + *userp = NULL; + + if (dp != NULL) { + n = strlen(dp) + 1; + strcpy(cp, dp); + *domainp = cp; + cp += n; + } else + *domainp = NULL; + + return (NGR_R_OK); +} +#else /* NGR_R_RETURN */ + static int getnetgrent_r_unknown_system = 0; +#endif /* NGR_R_RETURN */ +#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c b/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c new file mode 100644 index 0000000000..e56ad4ab5d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#if !defined(__BIND_NOSTATIC) + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> + +#include <irs.h> + +#include "port_after.h" + +#include "irs_data.h" + +/* Forward */ + +static struct net_data *init(void); + +/* Public */ + +struct protoent * +getprotoent() { + struct net_data *net_data = init(); + + return (getprotoent_p(net_data)); +} + +struct protoent * +getprotobyname(const char *name) { + struct net_data *net_data = init(); + + return (getprotobyname_p(name, net_data)); +} + +struct protoent * +getprotobynumber(int proto) { + struct net_data *net_data = init(); + + return (getprotobynumber_p(proto, net_data)); +} + +void +setprotoent(int stayopen) { + struct net_data *net_data = init(); + + setprotoent_p(stayopen, net_data); +} + +void +endprotoent() { + struct net_data *net_data = init(); + + endprotoent_p(net_data); +} + +/* Shared private. */ + +struct protoent * +getprotoent_p(struct net_data *net_data) { + struct irs_pr *pr; + + if (!net_data || !(pr = net_data->pr)) + return (NULL); + net_data->pr_last = (*pr->next)(pr); + return (net_data->pr_last); +} + +struct protoent * +getprotobyname_p(const char *name, struct net_data *net_data) { + struct irs_pr *pr; + char **pap; + + if (!net_data || !(pr = net_data->pr)) + return (NULL); + if (net_data->pr_stayopen && net_data->pr_last) { + if (!strcmp(net_data->pr_last->p_name, name)) + return (net_data->pr_last); + for (pap = net_data->pr_last->p_aliases; pap && *pap; pap++) + if (!strcmp(name, *pap)) + return (net_data->pr_last); + } + net_data->pr_last = (*pr->byname)(pr, name); + if (!net_data->pr_stayopen) + endprotoent(); + return (net_data->pr_last); +} + +struct protoent * +getprotobynumber_p(int proto, struct net_data *net_data) { + struct irs_pr *pr; + + if (!net_data || !(pr = net_data->pr)) + return (NULL); + if (net_data->pr_stayopen && net_data->pr_last) + if (net_data->pr_last->p_proto == proto) + return (net_data->pr_last); + net_data->pr_last = (*pr->bynumber)(pr, proto); + if (!net_data->pr_stayopen) + endprotoent(); + return (net_data->pr_last); +} + +void +setprotoent_p(int stayopen, struct net_data *net_data) { + struct irs_pr *pr; + + if (!net_data || !(pr = net_data->pr)) + return; + (*pr->rewind)(pr); + net_data->pr_stayopen = (stayopen != 0); + if (stayopen == 0) + net_data_minimize(net_data); +} + +void +endprotoent_p(struct net_data *net_data) { + struct irs_pr *pr; + + if ((net_data != NULL) && ((pr = net_data->pr) != NULL)) + (*pr->minimize)(pr); +} + +/* Private */ + +static struct net_data * +init() { + struct net_data *net_data; + + if (!(net_data = net_data_init(NULL))) + goto error; + if (!net_data->pr) { + net_data->pr = (*net_data->irs->pr_map)(net_data->irs); + + if (!net_data->pr || !net_data->res) { + error: + errno = EIO; + return (NULL); + } + (*net_data->pr->res_set)(net_data->pr, net_data->res, NULL); + } + + return (net_data); +} + +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c new file mode 100644 index 0000000000..b9a2623bf1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#if !defined(_REENTRANT) || !defined(DO_PTHREADS) + static int getprotoent_r_not_required = 0; +#else +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <port_after.h> + +#ifdef PROTO_R_RETURN + +static PROTO_R_RETURN +copy_protoent(struct protoent *, struct protoent *, PROTO_R_COPY_ARGS); + +PROTO_R_RETURN +getprotobyname_r(const char *name, struct protoent *pptr, PROTO_R_ARGS) { + struct protoent *pe = getprotobyname(name); +#ifdef PROTO_R_SETANSWER + int n = 0; + + if (pe == NULL || (n = copy_protoent(pe, pptr, PROTO_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = pptr; + + return (n); +#else + if (pe == NULL) + return (PROTO_R_BAD); + + return (copy_protoent(pe, pptr, PROTO_R_COPY)); +#endif +} + +PROTO_R_RETURN +getprotobynumber_r(int proto, struct protoent *pptr, PROTO_R_ARGS) { + struct protoent *pe = getprotobynumber(proto); +#ifdef PROTO_R_SETANSWER + int n = 0; + + if (pe == NULL || (n = copy_protoent(pe, pptr, PROTO_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = pptr; + + return (n); +#else + if (pe == NULL) + return (PROTO_R_BAD); + + return (copy_protoent(pe, pptr, PROTO_R_COPY)); +#endif +} + +/*% + * These assume a single context is in operation per thread. + * If this is not the case we will need to call irs directly + * rather than through the base functions. + */ + +PROTO_R_RETURN +getprotoent_r(struct protoent *pptr, PROTO_R_ARGS) { + struct protoent *pe = getprotoent(); +#ifdef PROTO_R_SETANSWER + int n = 0; + + if (pe == NULL || (n = copy_protoent(pe, pptr, PROTO_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = pptr; + + return (n); +#else + if (pe == NULL) + return (PROTO_R_BAD); + + return (copy_protoent(pe, pptr, PROTO_R_COPY)); +#endif +} + +PROTO_R_SET_RETURN +#ifdef PROTO_R_ENT_ARGS +setprotoent_r(int stay_open, PROTO_R_ENT_ARGS) +#else +setprotoent_r(int stay_open) +#endif +{ +#ifdef PROTO_R_ENT_UNUSED + PROTO_R_ENT_UNUSED; +#endif + setprotoent(stay_open); +#ifdef PROTO_R_SET_RESULT + return (PROTO_R_SET_RESULT); +#endif +} + +PROTO_R_END_RETURN +#ifdef PROTO_R_ENT_ARGS +endprotoent_r(PROTO_R_ENT_ARGS) +#else +endprotoent_r() +#endif +{ +#ifdef PROTO_R_ENT_UNUSED + PROTO_R_ENT_UNUSED; +#endif + endprotoent(); + PROTO_R_END_RESULT(PROTO_R_OK); +} + +/* Private */ + +#ifndef PROTOENT_DATA +static PROTO_R_RETURN +copy_protoent(struct protoent *pe, struct protoent *pptr, PROTO_R_COPY_ARGS) { + 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 (PROTO_R_BAD); + } + + /* 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 (PROTO_R_OK); +} +#else /* !PROTOENT_DATA */ +static int +copy_protoent(struct protoent *pe, struct protoent *pptr, PROTO_R_COPY_ARGS) { + char *cp, *eob; + int i, n; + + /* copy protocol value */ + pptr->p_proto = pe->p_proto; + + /* copy official name */ + cp = pdptr->line; + eob = pdptr->line + sizeof(pdptr->line); + if ((n = strlen(pe->p_name) + 1) < (eob - cp)) { + strcpy(cp, pe->p_name); + pptr->p_name = cp; + cp += n; + } else { + return (-1); + } + + /* copy aliases */ + i = 0; + pptr->p_aliases = pdptr->proto_aliases; + while (pe->p_aliases[i] && i < (_MAXALIASES-1)) { + if ((n = strlen(pe->p_aliases[i]) + 1) < (eob - cp)) { + strcpy(cp, pe->p_aliases[i]); + pptr->p_aliases[i] = cp; + cp += n; + } else { + break; + } + i++; + } + pptr->p_aliases[i] = NULL; + + return (PROTO_R_OK); +} +#endif /* PROTOENT_DATA */ +#else /* PROTO_R_RETURN */ + static int getprotoent_r_unknown_system = 0; +#endif /* PROTO_R_RETURN */ +#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getservent.c b/usr/src/lib/libresolv2_joy/common/irs/getservent.c new file mode 100644 index 0000000000..3cc2422b98 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getservent.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#if !defined(__BIND_NOSTATIC) + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> + +#include <irs.h> + +#include "port_after.h" + +#include "irs_data.h" + +/* Forward */ + +static struct net_data *init(void); + +/* Public */ + +struct servent * +getservent(void) { + struct net_data *net_data = init(); + + return (getservent_p(net_data)); +} + +struct servent * +getservbyname(const char *name, const char *proto) { + struct net_data *net_data = init(); + + return (getservbyname_p(name, proto, net_data)); +} + +struct servent * +getservbyport(int port, const char *proto) { + struct net_data *net_data = init(); + + return (getservbyport_p(port, proto, net_data)); +} + +void +setservent(int stayopen) { + struct net_data *net_data = init(); + + setservent_p(stayopen, net_data); +} + +void +endservent() { + struct net_data *net_data = init(); + + endservent_p(net_data); +} + +/* Shared private. */ + +struct servent * +getservent_p(struct net_data *net_data) { + struct irs_sv *sv; + + if (!net_data || !(sv = net_data->sv)) + return (NULL); + net_data->sv_last = (*sv->next)(sv); + return (net_data->sv_last); +} + +struct servent * +getservbyname_p(const char *name, const char *proto, + struct net_data *net_data) { + struct irs_sv *sv; + char **sap; + + if (!net_data || !(sv = net_data->sv)) + return (NULL); + if (net_data->sv_stayopen && net_data->sv_last) + if (!proto || !strcmp(net_data->sv_last->s_proto, proto)) { + if (!strcmp(net_data->sv_last->s_name, name)) + return (net_data->sv_last); + for (sap = net_data->sv_last->s_aliases; + sap && *sap; sap++) + if (!strcmp(name, *sap)) + return (net_data->sv_last); + } + net_data->sv_last = (*sv->byname)(sv, name, proto); + if (!net_data->sv_stayopen) + endservent(); + return (net_data->sv_last); +} + +struct servent * +getservbyport_p(int port, const char *proto, struct net_data *net_data) { + struct irs_sv *sv; + + if (!net_data || !(sv = net_data->sv)) + return (NULL); + if (net_data->sv_stayopen && net_data->sv_last) + if (port == net_data->sv_last->s_port && + ( !proto || + !strcmp(net_data->sv_last->s_proto, proto))) + return (net_data->sv_last); + net_data->sv_last = (*sv->byport)(sv, port, proto); + return (net_data->sv_last); +} + +void +setservent_p(int stayopen, struct net_data *net_data) { + struct irs_sv *sv; + + if (!net_data || !(sv = net_data->sv)) + return; + (*sv->rewind)(sv); + net_data->sv_stayopen = (stayopen != 0); + if (stayopen == 0) + net_data_minimize(net_data); +} + +void +endservent_p(struct net_data *net_data) { + struct irs_sv *sv; + + if ((net_data != NULL) && ((sv = net_data->sv) != NULL)) + (*sv->minimize)(sv); +} + +/* Private */ + +static struct net_data * +init() { + struct net_data *net_data; + + if (!(net_data = net_data_init(NULL))) + goto error; + if (!net_data->sv) { + net_data->sv = (*net_data->irs->sv_map)(net_data->irs); + + if (!net_data->sv || !net_data->res) { + error: + errno = EIO; + return (NULL); + } + (*net_data->sv->res_set)(net_data->sv, net_data->res, NULL); + } + + return (net_data); +} + +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c new file mode 100644 index 0000000000..91cebcf480 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#if !defined(_REENTRANT) || !defined(DO_PTHREADS) + static int getservent_r_not_required = 0; +#else +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/param.h> +#include <port_after.h> + +#ifdef SERV_R_RETURN + +static SERV_R_RETURN +copy_servent(struct servent *, struct servent *, SERV_R_COPY_ARGS); + +SERV_R_RETURN +getservbyname_r(const char *name, const char *proto, + struct servent *sptr, SERV_R_ARGS) { + struct servent *se = getservbyname(name, proto); +#ifdef SERV_R_SETANSWER + int n = 0; + + if (se == NULL || (n = copy_servent(se, sptr, SERV_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = sptr; + + return (n); +#else + if (se == NULL) + return (SERV_R_BAD); + + return (copy_servent(se, sptr, SERV_R_COPY)); +#endif +} + +SERV_R_RETURN +getservbyport_r(int port, const char *proto, + struct servent *sptr, SERV_R_ARGS) { + struct servent *se = getservbyport(port, proto); +#ifdef SERV_R_SETANSWER + int n = 0; + + if (se == NULL || (n = copy_servent(se, sptr, SERV_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = sptr; + + return (n); +#else + if (se == NULL) + return (SERV_R_BAD); + + return (copy_servent(se, sptr, SERV_R_COPY)); +#endif +} + +/*% + * These assume a single context is in operation per thread. + * If this is not the case we will need to call irs directly + * rather than through the base functions. + */ + +SERV_R_RETURN +getservent_r(struct servent *sptr, SERV_R_ARGS) { + struct servent *se = getservent(); +#ifdef SERV_R_SETANSWER + int n = 0; + + if (se == NULL || (n = copy_servent(se, sptr, SERV_R_COPY)) != 0) + *answerp = NULL; + else + *answerp = sptr; + + return (n); +#else + if (se == NULL) + return (SERV_R_BAD); + + return (copy_servent(se, sptr, SERV_R_COPY)); +#endif +} + +SERV_R_SET_RETURN +#ifdef SERV_R_ENT_ARGS +setservent_r(int stay_open, SERV_R_ENT_ARGS) +#else +setservent_r(int stay_open) +#endif +{ +#ifdef SERV_R_ENT_UNUSED + SERV_R_ENT_UNUSED; +#endif + setservent(stay_open); +#ifdef SERV_R_SET_RESULT + return (SERV_R_SET_RESULT); +#endif +} + +SERV_R_END_RETURN +#ifdef SERV_R_ENT_ARGS +endservent_r(SERV_R_ENT_ARGS) +#else +endservent_r() +#endif +{ +#ifdef SERV_R_ENT_UNUSED + SERV_R_ENT_UNUSED; +#endif + endservent(); + SERV_R_END_RESULT(SERV_R_OK); +} + +/* Private */ + +#ifndef SERVENT_DATA +static SERV_R_RETURN +copy_servent(struct servent *se, struct servent *sptr, SERV_R_COPY_ARGS) { + 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; + } + len += strlen(se->s_name) + 1; + len += strlen(se->s_proto) + 1; + len += numptr * sizeof(char*); + + if (len > (int)buflen) { + errno = ERANGE; + return (SERV_R_BAD); + } + + /* copy port value */ + sptr->s_port = se->s_port; + + cp = (char *)ALIGN(buf) + numptr * sizeof(char *); + + /* copy official name */ + n = strlen(se->s_name) + 1; + strcpy(cp, se->s_name); + sptr->s_name = cp; + cp += n; + + /* 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; + } + sptr->s_aliases[i] = NULL; + + /* copy proto */ + n = strlen(se->s_proto) + 1; + strcpy(cp, se->s_proto); + sptr->s_proto = cp; + cp += n; + + return (SERV_R_OK); +} +#else /* !SERVENT_DATA */ +static int +copy_servent(struct servent *se, struct servent *sptr, SERV_R_COPY_ARGS) { + char *cp, *eob; + int i, n; + + /* copy port value */ + sptr->s_port = se->s_port; + + /* copy official name */ + cp = sdptr->line; + eob = sdptr->line + sizeof(sdptr->line); + if ((n = strlen(se->s_name) + 1) < (eob - cp)) { + strcpy(cp, se->s_name); + sptr->s_name = cp; + cp += n; + } else { + return (-1); + } + + /* copy aliases */ + i = 0; + sptr->s_aliases = sdptr->serv_aliases; + while (se->s_aliases[i] && i < (_MAXALIASES-1)) { + if ((n = strlen(se->s_aliases[i]) + 1) < (eob - cp)) { + strcpy(cp, se->s_aliases[i]); + sptr->s_aliases[i] = cp; + cp += n; + } else { + break; + } + i++; + } + sptr->s_aliases[i] = NULL; + + /* copy proto */ + if ((n = strlen(se->s_proto) + 1) < (eob - cp)) { + strcpy(cp, se->s_proto); + sptr->s_proto = cp; + cp += n; + } else { + return (-1); + } + + return (SERV_R_OK); +} +#endif /* !SERVENT_DATA */ +#else /*SERV_R_RETURN */ + static int getservent_r_unknown_system = 0; +#endif /*SERV_R_RETURN */ +#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/hesiod.c b/usr/src/lib/libresolv2_joy/common/irs/hesiod.c new file mode 100644 index 0000000000..2dd05431a5 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/hesiod.c @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + + +/*! \file + * \brief + * hesiod.c --- the core portion of the hesiod resolver. + * + * This file is derived from the hesiod library from Project Athena; + * It has been extensively rewritten by Theodore Ts'o to have a more + * thread-safe interface. + * \author + * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "port_after.h" + +#include "pathnames.h" +#include "hesiod.h" +#include "hesiod_p.h" + +/* Forward */ + +int hesiod_init(void **context); +void hesiod_end(void *context); +char * hesiod_to_bind(void *context, const char *name, + const char *type); +char ** hesiod_resolve(void *context, const char *name, + const char *type); +void hesiod_free_list(void *context, char **list); + +static int parse_config_file(struct hesiod_p *ctx, const char *filename); +static char ** get_txt_records(struct hesiod_p *ctx, int class, + const char *name); +static int init(struct hesiod_p *ctx); + +/* Public */ + +/*% + * This function is called to initialize a hesiod_p. + */ +int +hesiod_init(void **context) { + struct hesiod_p *ctx; + char *cp; + + ctx = malloc(sizeof(struct hesiod_p)); + if (ctx == 0) { + errno = ENOMEM; + return (-1); + } + + memset(ctx, 0, sizeof (*ctx)); + + if (parse_config_file(ctx, _PATH_HESIOD_CONF) < 0) { +#ifdef DEF_RHS + /* + * Use compiled in defaults. + */ + ctx->LHS = malloc(strlen(DEF_LHS) + 1); + ctx->RHS = malloc(strlen(DEF_RHS) + 1); + if (ctx->LHS == NULL || ctx->RHS == NULL) { + errno = ENOMEM; + goto cleanup; + } + strcpy(ctx->LHS, DEF_LHS); /* (checked) */ + strcpy(ctx->RHS, DEF_RHS); /* (checked) */ +#else + goto cleanup; +#endif + } + /* + * The default RHS can be overridden by an environment + * variable. + */ + if ((cp = getenv("HES_DOMAIN")) != NULL) { + size_t RHSlen = strlen(cp) + 2; + if (ctx->RHS) + free(ctx->RHS); + ctx->RHS = malloc(RHSlen); + if (!ctx->RHS) { + errno = ENOMEM; + goto cleanup; + } + if (cp[0] == '.') { + strcpy(ctx->RHS, cp); /* (checked) */ + } else { + strcpy(ctx->RHS, "."); /* (checked) */ + strcat(ctx->RHS, cp); /* (checked) */ + } + } + + /* + * If there is no default hesiod realm set, we return an + * error. + */ + if (!ctx->RHS) { + errno = ENOEXEC; + goto cleanup; + } + +#if 0 + if (res_ninit(ctx->res) < 0) + goto cleanup; +#endif + + *context = ctx; + return (0); + + cleanup: + hesiod_end(ctx); + return (-1); +} + +/*% + * This function deallocates the hesiod_p + */ +void +hesiod_end(void *context) { + struct hesiod_p *ctx = (struct hesiod_p *) context; + int save_errno = errno; + + if (ctx->res) + res_nclose(ctx->res); + if (ctx->RHS) + free(ctx->RHS); + if (ctx->LHS) + free(ctx->LHS); + if (ctx->res && ctx->free_res) + (*ctx->free_res)(ctx->res); + free(ctx); + errno = save_errno; +} + +/*% + * This function takes a hesiod (name, type) and returns a DNS + * name which is to be resolved. + */ +char * +hesiod_to_bind(void *context, const char *name, const char *type) { + struct hesiod_p *ctx = (struct hesiod_p *) context; + char *bindname; + char **rhs_list = NULL; + const char *RHS, *cp; + + /* Decide what our RHS is, and set cp to the end of the actual name. */ + if ((cp = strchr(name, '@')) != NULL) { + if (strchr(cp + 1, '.')) + RHS = cp + 1; + else if ((rhs_list = hesiod_resolve(context, cp + 1, + "rhs-extension")) != NULL) + RHS = *rhs_list; + else { + errno = ENOENT; + return (NULL); + } + } else { + RHS = ctx->RHS; + cp = name + strlen(name); + } + + /* + * Allocate the space we need, including up to three periods and + * the terminating NUL. + */ + if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) + + (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) { + errno = ENOMEM; + if (rhs_list) + hesiod_free_list(context, rhs_list); + return NULL; + } + + /* Now put together the DNS name. */ + memcpy(bindname, name, cp - name); + bindname[cp - name] = '\0'; + strcat(bindname, "."); + strcat(bindname, type); + if (ctx->LHS) { + if (ctx->LHS[0] != '.') + strcat(bindname, "."); + strcat(bindname, ctx->LHS); + } + if (RHS[0] != '.') + strcat(bindname, "."); + strcat(bindname, RHS); + + if (rhs_list) + hesiod_free_list(context, rhs_list); + + return (bindname); +} + +/*% + * This is the core function. Given a hesiod (name, type), it + * returns an array of strings returned by the resolver. + */ +char ** +hesiod_resolve(void *context, const char *name, const char *type) { + struct hesiod_p *ctx = (struct hesiod_p *) context; + char *bindname = hesiod_to_bind(context, name, type); + char **retvec; + + if (bindname == NULL) + return (NULL); + if (init(ctx) == -1) { + free(bindname); + return (NULL); + } + + if ((retvec = get_txt_records(ctx, C_IN, bindname))) { + free(bindname); + return (retvec); + } + + if (errno != ENOENT) + return (NULL); + + retvec = get_txt_records(ctx, C_HS, bindname); + free(bindname); + return (retvec); +} + +void +hesiod_free_list(void *context, char **list) { + char **p; + + UNUSED(context); + + for (p = list; *p; p++) + free(*p); + free(list); +} + +/*% + * This function parses the /etc/hesiod.conf file + */ +static int +parse_config_file(struct hesiod_p *ctx, const char *filename) { + char *key, *data, *cp, **cpp; + char buf[MAXDNAME+7]; + FILE *fp; + + /* + * Clear the existing configuration variable, just in case + * they're set. + */ + if (ctx->RHS) + free(ctx->RHS); + if (ctx->LHS) + free(ctx->LHS); + ctx->RHS = ctx->LHS = 0; + + /* + * Now open and parse the file... + */ + if (!(fp = fopen(filename, "r"))) + return (-1); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + cp = buf; + if (*cp == '#' || *cp == '\n' || *cp == '\r') + continue; + while(*cp == ' ' || *cp == '\t') + cp++; + key = cp; + while(*cp != ' ' && *cp != '\t' && *cp != '=') + cp++; + *cp++ = '\0'; + + while(*cp == ' ' || *cp == '\t' || *cp == '=') + cp++; + data = cp; + while(*cp != ' ' && *cp != '\n' && *cp != '\r') + cp++; + *cp++ = '\0'; + + if (strcmp(key, "lhs") == 0) + cpp = &ctx->LHS; + else if (strcmp(key, "rhs") == 0) + cpp = &ctx->RHS; + else + continue; + + *cpp = malloc(strlen(data) + 1); + if (!*cpp) { + errno = ENOMEM; + goto cleanup; + } + strcpy(*cpp, data); + } + fclose(fp); + return (0); + + cleanup: + fclose(fp); + if (ctx->RHS) + free(ctx->RHS); + if (ctx->LHS) + free(ctx->LHS); + ctx->RHS = ctx->LHS = 0; + return (-1); +} + +/*% + * Given a DNS class and a DNS name, do a lookup for TXT records, and + * return a list of them. + */ +static char ** +get_txt_records(struct hesiod_p *ctx, int class, const char *name) { + struct { + int type; /*%< RR type */ + int class; /*%< RR class */ + int dlen; /*%< len of data section */ + u_char *data; /*%< pointer to data */ + } rr; + HEADER *hp; + u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP]; + u_char *cp, *erdata, *eom; + char *dst, *edst, **list; + int ancount, qdcount; + int i, j, n, skip; + + /* + * Construct the query and send it. + */ + n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0, + NULL, qbuf, MAX_HESRESP); + if (n < 0) { + errno = EMSGSIZE; + return (NULL); + } + n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP); + if (n < 0) { + errno = ECONNREFUSED; + return (NULL); + } + if (n < HFIXEDSZ) { + errno = EMSGSIZE; + return (NULL); + } + + /* + * OK, parse the result. + */ + hp = (HEADER *) abuf; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + cp = abuf + sizeof(HEADER); + eom = abuf + n; + + /* Skip query, trying to get to the answer section which follows. */ + for (i = 0; i < qdcount; i++) { + skip = dn_skipname(cp, eom); + if (skip < 0 || cp + skip + QFIXEDSZ > eom) { + errno = EMSGSIZE; + return (NULL); + } + cp += skip + QFIXEDSZ; + } + + list = malloc((ancount + 1) * sizeof(char *)); + if (!list) { + errno = ENOMEM; + return (NULL); + } + j = 0; + for (i = 0; i < ancount; i++) { + skip = dn_skipname(cp, eom); + if (skip < 0) { + errno = EMSGSIZE; + goto cleanup; + } + cp += skip; + if (cp + 3 * INT16SZ + INT32SZ > eom) { + errno = EMSGSIZE; + goto cleanup; + } + rr.type = ns_get16(cp); + cp += INT16SZ; + rr.class = ns_get16(cp); + cp += INT16SZ + INT32SZ; /*%< skip the ttl, too */ + rr.dlen = ns_get16(cp); + cp += INT16SZ; + if (cp + rr.dlen > eom) { + errno = EMSGSIZE; + goto cleanup; + } + rr.data = cp; + cp += rr.dlen; + if (rr.class != class || rr.type != T_TXT) + continue; + if (!(list[j] = malloc(rr.dlen))) + goto cleanup; + dst = list[j++]; + edst = dst + rr.dlen; + erdata = rr.data + rr.dlen; + cp = rr.data; + while (cp < erdata) { + n = (unsigned char) *cp++; + if (cp + n > eom || dst + n > edst) { + errno = EMSGSIZE; + goto cleanup; + } + memcpy(dst, cp, n); + cp += n; + dst += n; + } + if (cp != erdata) { + errno = EMSGSIZE; + goto cleanup; + } + *dst = '\0'; + } + list[j] = NULL; + if (j == 0) { + errno = ENOENT; + goto cleanup; + } + return (list); + + cleanup: + for (i = 0; i < j; i++) + free(list[i]); + free(list); + return (NULL); +} + +struct __res_state * +__hesiod_res_get(void *context) { + struct hesiod_p *ctx = context; + + if (!ctx->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (res == NULL) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + __hesiod_res_set(ctx, res, free); + } + + return (ctx->res); +} + +void +__hesiod_res_set(void *context, struct __res_state *res, + void (*free_res)(void *)) { + struct hesiod_p *ctx = context; + + if (ctx->res && ctx->free_res) { + res_nclose(ctx->res); + (*ctx->free_res)(ctx->res); + } + + ctx->res = res; + ctx->free_res = free_res; +} + +static int +init(struct hesiod_p *ctx) { + + if (!ctx->res && !__hesiod_res_get(ctx)) + return (-1); + + if (((ctx->res->options & RES_INIT) == 0U) && + (res_ninit(ctx->res) == -1)) + return (-1); + + return (0); +} diff --git a/usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h b/usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h new file mode 100644 index 0000000000..99da15d0cd --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: hesiod_p.h,v 1.3 2005/04/27 04:56:27 sra Exp $ + */ + +#ifndef _HESIOD_P_H_INCLUDED +#define _HESIOD_P_H_INCLUDED + +/** \file + * \brief + * hesiod_p.h -- private definitions for the hesiod library. + * + * \author + * This file is primarily maintained by tytso@mit.edu and ghudson@mit.edu. + */ + +#define DEF_RHS ".Athena.MIT.EDU" /*%< Defaults if HESIOD_CONF */ +#define DEF_LHS ".ns" /*%< file is not */ + /*%< present. */ +struct hesiod_p { + char * LHS; /*%< normally ".ns" */ + char * RHS; /*%< AKA the default hesiod domain */ + struct __res_state * res; /*%< resolver context */ + void (*free_res)(void *); + void (*res_set)(struct hesiod_p *, struct __res_state *, + void (*)(void *)); + struct __res_state * (*res_get)(struct hesiod_p *); +}; + +#define MAX_HESRESP 1024 + +#endif /*_HESIOD_P_H_INCLUDED*/ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp.c b/usr/src/lib/libresolv2_joy/common/irs/irp.c new file mode 100644 index 0000000000..07659c49af --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp.c @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1996, 1998-2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdarg.h> +#include <fcntl.h> +#include <syslog.h> +#include <ctype.h> +#include <unistd.h> + +#include <isc/memcluster.h> + +#include <irs.h> +#include <irp.h> + +#include "irs_p.h" +#include "irp_p.h" + +#include "port_after.h" + +/* Forward. */ + +static void irp_close(struct irs_acc *); + +#define LINEINCR 128 + +#if !defined(SUN_LEN) +#define SUN_LEN(su) \ + (sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path)) +#endif + + +/* Public */ + + +/* send errors to syslog if true. */ +int irp_log_errors = 1; + +/*% + * This module handles the irp module connection to irpd. + * + * The client expects a synchronous interface to functions like + * getpwnam(3), so we can't use the ctl_* i/o library on this end of + * the wire (it's used in the server). + */ + +/*% + * irs_acc *irs_irp_acc(const char *options); + * + * Initialize the irp module. + */ +struct irs_acc * +irs_irp_acc(const char *options) { + struct irs_acc *acc; + struct irp_p *irp; + + UNUSED(options); + + if (!(acc = memget(sizeof *acc))) { + errno = ENOMEM; + return (NULL); + } + memset(acc, 0x5e, sizeof *acc); + if (!(irp = memget(sizeof *irp))) { + errno = ENOMEM; + free(acc); + return (NULL); + } + irp->inlast = 0; + irp->incurr = 0; + irp->fdCxn = -1; + acc->private = irp; + +#ifdef WANT_IRS_GR + acc->gr_map = irs_irp_gr; +#else + acc->gr_map = NULL; +#endif +#ifdef WANT_IRS_PW + acc->pw_map = irs_irp_pw; +#else + acc->pw_map = NULL; +#endif + acc->sv_map = irs_irp_sv; + acc->pr_map = irs_irp_pr; + acc->ho_map = irs_irp_ho; + acc->nw_map = irs_irp_nw; + acc->ng_map = irs_irp_ng; + acc->close = irp_close; + return (acc); +} + + +int +irs_irp_connection_setup(struct irp_p *cxndata, int *warned) { + if (irs_irp_is_connected(cxndata)) { + return (0); + } else if (irs_irp_connect(cxndata) != 0) { + if (warned != NULL && !*warned) { + syslog(LOG_ERR, "irpd connection failed: %m\n"); + (*warned)++; + } + + return (-1); + } + + return (0); +} + +/*% + * int irs_irp_connect(void); + * + * Sets up the connection to the remote irpd server. + * + * Returns: + * + * 0 on success, -1 on failure. + * + */ +int +irs_irp_connect(struct irp_p *pvt) { + int flags; + struct sockaddr *addr; + struct sockaddr_in iaddr; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un uaddr; +#endif + long ipaddr; + const char *irphost; + int code; + char text[256]; + int socklen = 0; + + if (pvt->fdCxn != -1) { + perror("fd != 1"); + return (-1); + } + +#ifndef NO_SOCKADDR_UN + memset(&uaddr, 0, sizeof uaddr); +#endif + memset(&iaddr, 0, sizeof iaddr); + + irphost = getenv(IRPD_HOST_ENV); + if (irphost == NULL) { + irphost = "127.0.0.1"; + } + +#ifndef NO_SOCKADDR_UN + if (irphost[0] == '/') { + addr = (struct sockaddr *)&uaddr; + strncpy(uaddr.sun_path, irphost, sizeof uaddr.sun_path); + uaddr.sun_family = AF_UNIX; + socklen = SUN_LEN(&uaddr); +#ifdef HAVE_SA_LEN + uaddr.sun_len = socklen; +#endif + } else +#endif + { + if (inet_pton(AF_INET, irphost, &ipaddr) != 1) { + errno = EADDRNOTAVAIL; + perror("inet_pton"); + return (-1); + } + + addr = (struct sockaddr *)&iaddr; + socklen = sizeof iaddr; +#ifdef HAVE_SA_LEN + iaddr.sin_len = socklen; +#endif + iaddr.sin_family = AF_INET; + iaddr.sin_port = htons(IRPD_PORT); + iaddr.sin_addr.s_addr = ipaddr; + } + + + pvt->fdCxn = socket(addr->sa_family, SOCK_STREAM, PF_UNSPEC); + if (pvt->fdCxn < 0) { + perror("socket"); + return (-1); + } + + if (connect(pvt->fdCxn, addr, socklen) != 0) { + perror("connect"); + return (-1); + } + + flags = fcntl(pvt->fdCxn, F_GETFL, 0); + if (flags < 0) { + close(pvt->fdCxn); + perror("close"); + return (-1); + } + +#if 0 + flags |= O_NONBLOCK; + if (fcntl(pvt->fdCxn, F_SETFL, flags) < 0) { + close(pvt->fdCxn); + perror("fcntl"); + return (-1); + } +#endif + + code = irs_irp_read_response(pvt, text, sizeof text); + if (code != IRPD_WELCOME_CODE) { + if (irp_log_errors) { + syslog(LOG_WARNING, "Connection failed: %s", text); + } + irs_irp_disconnect(pvt); + return (-1); + } + + return (0); +} + +/*% + * int irs_irp_is_connected(struct irp_p *pvt); + * + * Returns: + * + * Non-zero if streams are setup to remote. + * + */ + +int +irs_irp_is_connected(struct irp_p *pvt) { + return (pvt->fdCxn >= 0); +} + +/*% + * void + * irs_irp_disconnect(struct irp_p *pvt); + * + * Closes streams to remote. + */ + +void +irs_irp_disconnect(struct irp_p *pvt) { + if (pvt->fdCxn != -1) { + close(pvt->fdCxn); + pvt->fdCxn = -1; + } +} + + + +int +irs_irp_read_line(struct irp_p *pvt, char *buffer, int len) { + char *realstart = &pvt->inbuffer[0]; + char *p, *start, *end; + int spare; + int i; + int buffpos = 0; + int left = len - 1; + + while (left > 0) { + start = p = &pvt->inbuffer[pvt->incurr]; + end = &pvt->inbuffer[pvt->inlast]; + + while (p != end && *p != '\n') + p++; + + if (p == end) { + /* Found no newline so shift data down if necessary + * and append new data to buffer + */ + if (start > realstart) { + memmove(realstart, start, end - start); + pvt->inlast = end - start; + start = realstart; + pvt->incurr = 0; + end = &pvt->inbuffer[pvt->inlast]; + } + + spare = sizeof (pvt->inbuffer) - pvt->inlast; + + p = end; + i = read(pvt->fdCxn, end, spare); + if (i < 0) { + close(pvt->fdCxn); + pvt->fdCxn = -1; + return (buffpos > 0 ? buffpos : -1); + } else if (i == 0) { + return (buffpos); + } + + end += i; + pvt->inlast += i; + + while (p != end && *p != '\n') + p++; + } + + if (p == end) { + /* full buffer and still no newline */ + i = sizeof pvt->inbuffer; + } else { + /* include newline */ + i = p - start + 1; + } + + if (i > left) + i = left; + memcpy(buffer + buffpos, start, i); + pvt->incurr += i; + buffpos += i; + buffer[buffpos] = '\0'; + + if (p != end) { + left = 0; + } else { + left -= i; + } + } + +#if 0 + fprintf(stderr, "read line: %s\n", buffer); +#endif + return (buffpos); +} + +/*% + * int irp_read_response(struct irp_p *pvt); + * + * Returns: + * + * The number found at the beginning of the line read from + * FP. 0 on failure(0 is not a legal response code). The + * rest of the line is discarded. + * + */ + +int +irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen) { + char line[1024]; + int code; + char *p; + + if (irs_irp_read_line(pvt, line, sizeof line) <= 0) { + return (0); + } + + p = strchr(line, '\n'); + if (p == NULL) { + return (0); + } + + if (sscanf(line, "%d", &code) != 1) { + code = 0; + } else if (text != NULL && textlen > 0U) { + p = line; + while (isspace((unsigned char)*p)) p++; + while (isdigit((unsigned char)*p)) p++; + while (isspace((unsigned char)*p)) p++; + strncpy(text, p, textlen - 1); + p[textlen - 1] = '\0'; + } + + return (code); +} + +/*% + * char *irp_read_body(struct irp_p *pvt, size_t *size); + * + * Read in the body of a response. Terminated by a line with + * just a dot on it. Lines should be terminated with a CR-LF + * sequence, but we're nt piccky if the CR is missing. + * No leading dot escaping is done as the protcol doesn't + * use leading dots anywhere. + * + * Returns: + * + * Pointer to null-terminated buffer allocated by memget. + * *SIZE is set to the length of the buffer. + * + */ + +char * +irs_irp_read_body(struct irp_p *pvt, size_t *size) { + char line[1024]; + u_int linelen; + size_t len = LINEINCR; + char *buffer = memget(len); + int idx = 0; + + if (buffer == NULL) + return (NULL); + + for (;;) { + if (irs_irp_read_line(pvt, line, sizeof line) <= 0 || + strchr(line, '\n') == NULL) + goto death; + + linelen = strlen(line); + + if (line[linelen - 1] != '\n') + goto death; + + /* We're not strict about missing \r. Should we be?? */ + if (linelen > 2 && line[linelen - 2] == '\r') { + line[linelen - 2] = '\n'; + line[linelen - 1] = '\0'; + linelen--; + } + + if (linelen == 2 && line[0] == '.') { + *size = len; + buffer[idx] = '\0'; + + return (buffer); + } + + if (linelen > (len - (idx + 1))) { + char *p = memget(len + LINEINCR); + + if (p == NULL) + goto death; + memcpy(p, buffer, len); + memput(buffer, len); + buffer = p; + len += LINEINCR; + } + + memcpy(buffer + idx, line, linelen); + idx += linelen; + } + death: + memput(buffer, len); + return (NULL); +} + +/*% + * int irs_irp_get_full_response(struct irp_p *pvt, int *code, + * char **body, size_t *bodylen); + * + * Gets the response to a command. If the response indicates + * there's a body to follow(code % 10 == 1), then the + * body buffer is allcoated with memget and stored in + * *BODY. The length of the allocated body buffer is stored + * in *BODY. The caller must give the body buffer back to + * memput when done. The results code is stored in *CODE. + * + * Returns: + * + * 0 if a result was read. -1 on some sort of failure. + * + */ + +int +irs_irp_get_full_response(struct irp_p *pvt, int *code, char *text, + size_t textlen, char **body, size_t *bodylen) { + int result = irs_irp_read_response(pvt, text, textlen); + + *body = NULL; + + if (result == 0) { + return (-1); + } + + *code = result; + + /* Code that matches 2xx is a good result code. + * Code that matches xx1 means there's a response body coming. + */ + if ((result / 100) == 2 && (result % 10) == 1) { + *body = irs_irp_read_body(pvt, bodylen); + if (*body == NULL) { + return (-1); + } + } + + return (0); +} + +/*% + * int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...); + * + * Sends command to remote connected via the PVT + * structure. FMT and args after it are fprintf-like + * arguments for formatting. + * + * Returns: + * + * 0 on success, -1 on failure. + */ + +int +irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...) { + va_list ap; + char buffer[1024]; + int pos = 0; + int i, todo; + + + if (pvt->fdCxn < 0) { + return (-1); + } + + va_start(ap, fmt); + (void) vsprintf(buffer, fmt, ap); + todo = strlen(buffer); + va_end(ap); + if (todo > (int)sizeof(buffer) - 3) { + syslog(LOG_CRIT, "memory overrun in irs_irp_send_command()"); + exit(1); + } + strcat(buffer, "\r\n"); + todo = strlen(buffer); + + while (todo > 0) { + i = write(pvt->fdCxn, buffer + pos, todo); +#if 0 + /* XXX brister */ + fprintf(stderr, "Wrote: \""); + fwrite(buffer + pos, sizeof (char), todo, stderr); + fprintf(stderr, "\"\n"); +#endif + if (i < 0) { + close(pvt->fdCxn); + pvt->fdCxn = -1; + return (-1); + } + todo -= i; + } + + return (0); +} + + +/* Methods */ + +/*% + * void irp_close(struct irs_acc *this) + * + */ + +static void +irp_close(struct irs_acc *this) { + struct irp_p *irp = (struct irp_p *)this->private; + + if (irp != NULL) { + irs_irp_disconnect(irp); + memput(irp, sizeof *irp); + } + + memput(this, sizeof *this); +} + + + + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c b/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c new file mode 100644 index 0000000000..7a13d2dfc6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996,1998 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports. */ + +#include "port_before.h" + +#include <syslog.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <irs.h> +#include <irp.h> +#include <isc/irpmarshall.h> +#include <isc/memcluster.h> + +#include "irs_p.h" +#include "dns_p.h" +#include "irp_p.h" + +#include "port_after.h" + +/* Definitions. */ + +#define MAXALIASES 35 +#define MAXADDRS 35 +#define Max(a,b) ((a) > (b) ? (a) : (b)) + + +struct pvt { + struct irp_p *girpdata; + int warned; + struct hostent host; +}; + +/* Forward. */ + +static void ho_close(struct irs_ho *this); +static struct hostent * ho_byname(struct irs_ho *this, const char *name); +static struct hostent * ho_byname2(struct irs_ho *this, const char *name, + int af); +static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, + int len, int af); +static struct hostent * ho_next(struct irs_ho *this); +static void ho_rewind(struct irs_ho *this); +static void ho_minimize(struct irs_ho *this); + +static void free_host(struct hostent *ho); +static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, + const struct addrinfo *pai); + +/* Public. */ + +/*% + * struct irs_ho * irs_irp_ho(struct irs_acc *this) + * + * Notes: + * + * Initializes the irp_ho module. + * + */ + +struct irs_ho * +irs_irp_ho(struct irs_acc *this) { + struct irs_ho *ho; + struct pvt *pvt; + + if (!(ho = memget(sizeof *ho))) { + errno = ENOMEM; + return (NULL); + } + memset(ho, 0x0, sizeof *ho); + + if (!(pvt = memget(sizeof *pvt))) { + memput(ho, sizeof *ho); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->girpdata = this->private; + + ho->private = pvt; + ho->close = ho_close; + ho->byname = ho_byname; + ho->byname2 = ho_byname2; + ho->byaddr = ho_byaddr; + ho->next = ho_next; + ho->rewind = ho_rewind; + ho->minimize = ho_minimize; + ho->addrinfo = ho_addrinfo; + + return (ho); +} + +/* Methods. */ + +/*% + * Closes down the module. + * + */ + +static void +ho_close(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + ho_minimize(this); + + free_host(&pvt->host); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + + + +/* + * struct hostent * ho_byname(struct irs_ho *this, const char *name) + * + */ + +static struct hostent * +ho_byname(struct irs_ho *this, const char *name) { + return (ho_byname2(this, name, AF_INET)); +} + + + + + +/* + * struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af) + * + */ + +static struct hostent * +ho_byname2(struct irs_ho *this, const char *name, int af) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *ho = &pvt->host; + char *body = NULL; + size_t bodylen; + int code; + char text[256]; + + if (ho->h_name != NULL && + strcmp(name, ho->h_name) == 0 && + af == ho->h_addrtype) { + return (ho); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "gethostbyname2 %s %s", + name, ADDR_T_STR(af)) != 0) + return (NULL); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETHOST_OK) { + free_host(ho); + if (irp_unmarshall_ho(ho, body) != 0) { + ho = NULL; + } + } else { + ho = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (ho); +} + + + +/* + * struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, + * int len, int af) + * + */ + +static struct hostent * +ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *ho = &pvt->host; + char *body = NULL; + size_t bodylen; + int code; + char **p; + char paddr[MAXPADDRSIZE]; + char text[256]; + + if (ho->h_name != NULL && + af == ho->h_addrtype && + len == ho->h_length) { + for (p = ho->h_addr_list ; *p != NULL ; p++) { + if (memcmp(*p, addr, len) == 0) + return (ho); + } + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (inet_ntop(af, addr, paddr, sizeof paddr) == NULL) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "gethostbyaddr %s %s", + paddr, ADDR_T_STR(af)) != 0) { + return (NULL); + } + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETHOST_OK) { + free_host(ho); + if (irp_unmarshall_ho(ho, body) != 0) { + ho = NULL; + } + } else { + ho = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (ho); +} + +/*% + * The implementation for gethostent(3). The first time it's + * called all the data is pulled from the remote(i.e. what + * the maximum number of gethostent(3) calls would return) + * and that data is cached. + * + */ + +static struct hostent * +ho_next(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *ho = &pvt->host; + char *body; + size_t bodylen; + int code; + char text[256]; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "gethostent") != 0) { + return (NULL); + } + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETHOST_OK) { + free_host(ho); + if (irp_unmarshall_ho(ho, body) != 0) { + ho = NULL; + } + } else { + ho = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (ho); +} + +/*% + * void ho_rewind(struct irs_ho *this) + * + */ + +static void +ho_rewind(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + char text[256]; + int code; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return; + } + + if (irs_irp_send_command(pvt->girpdata, "sethostent") != 0) { + return; + } + + code = irs_irp_read_response(pvt->girpdata, text, sizeof text); + if (code != IRPD_GETHOST_SETOK) { + if (irp_log_errors) { + syslog(LOG_WARNING, "sethostent failed: %s", text); + } + } + + return; +} + +/*% + * void ho_minimize(struct irs_ho *this) + * + */ + +static void +ho_minimize(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + free_host(&pvt->host); + + irs_irp_disconnect(pvt->girpdata); +} + +/*% + * void free_host(struct hostent *ho) + * + */ + +static void +free_host(struct hostent *ho) { + char **p; + + if (ho == NULL) { + return; + } + + if (ho->h_name != NULL) + free(ho->h_name); + + if (ho->h_aliases != NULL) { + for (p = ho->h_aliases ; *p != NULL ; p++) + free(*p); + free(ho->h_aliases); + } + + if (ho->h_addr_list != NULL) { + for (p = ho->h_addr_list ; *p != NULL ; p++) + free(*p); + free(ho->h_addr_list); + } +} + +/* dummy */ +static struct addrinfo * +ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) +{ + UNUSED(this); + UNUSED(name); + UNUSED(pai); + return(NULL); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c b/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c new file mode 100644 index 0000000000..ef5e5db67c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> + +#include <irs.h> +#include <irp.h> +#include <isc/memcluster.h> +#include <isc/irpmarshall.h> + +#include "irs_p.h" +#include "irp_p.h" + +#include "port_after.h" + +/* Definitions */ + +struct pvt { + struct irp_p *girpdata; + int warned; +}; + + +/* Forward */ + +static void ng_rewind(struct irs_ng *, const char*); +static void ng_close(struct irs_ng *); +static int ng_next(struct irs_ng *, const char **, const char **, + const char **); +static int ng_test(struct irs_ng *, const char *, + const char *, const char *, + const char *); +static void ng_minimize(struct irs_ng *); + + +/* Public */ + +/*% + * Intialize the irp netgroup module. + * + */ + +struct irs_ng * +irs_irp_ng(struct irs_acc *this) { + struct irs_ng *ng; + struct pvt *pvt; + + if (!(ng = memget(sizeof *ng))) { + errno = ENOMEM; + return (NULL); + } + memset(ng, 0x5e, sizeof *ng); + + if (!(pvt = memget(sizeof *pvt))) { + memput(ng, sizeof *ng); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->girpdata = this->private; + + ng->private = pvt; + ng->close = ng_close; + ng->next = ng_next; + ng->test = ng_test; + ng->rewind = ng_rewind; + ng->minimize = ng_minimize; + return (ng); +} + +/* Methods */ + + + +/* + * void ng_close(struct irs_ng *this) + * + */ + +static void +ng_close(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + + ng_minimize(this); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + + + + +/* + * void ng_rewind(struct irs_ng *this, const char *group) + * + * + */ + +static void +ng_rewind(struct irs_ng *this, const char *group) { + struct pvt *pvt = (struct pvt *)this->private; + char text[256]; + int code; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return; + } + + if (irs_irp_send_command(pvt->girpdata, + "setnetgrent %s", group) != 0) { + return; + } + + code = irs_irp_read_response(pvt->girpdata, text, sizeof text); + if (code != IRPD_GETNETGR_SETOK) { + if (irp_log_errors) { + syslog(LOG_WARNING, "setnetgrent(%s) failed: %s", + group, text); + } + } + + return; +} + +/* + * Get the next netgroup item from the cache. + * + */ + +static int +ng_next(struct irs_ng *this, const char **host, const char **user, + const char **domain) +{ + struct pvt *pvt = (struct pvt *)this->private; + int code; + char *body = NULL; + size_t bodylen; + int rval = 0; + char text[256]; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (0); + } + + if (irs_irp_send_command(pvt->girpdata, "getnetgrent") != 0) + return (0); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (0); + } + + if (code == IRPD_GETNETGR_OK) { + if (irp_unmarshall_ng(host, user, domain, body) == 0) { + rval = 1; + } + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (rval); +} + +/* + * Search for a match in a netgroup. + * + */ + +static int +ng_test(struct irs_ng *this, const char *name, + const char *host, const char *user, const char *domain) +{ + struct pvt *pvt = (struct pvt *)this->private; + char *body = NULL; + size_t bodylen = 0; + int code; + char text[256]; + int rval = 0; + + UNUSED(name); + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (0); + } + + if (irp_marshall_ng(host, user, domain, &body, &bodylen) != 0) { + return (0); + } + + if (irs_irp_send_command(pvt->girpdata, "innetgr %s", body) == 0) { + code = irs_irp_read_response(pvt->girpdata, text, sizeof text); + if (code == IRPD_GETNETGR_MATCHES) { + rval = 1; + } + } + + memput(body, bodylen); + + return (rval); +} + + + + +/* + * void ng_minimize(struct irs_ng *this) + * + */ + +static void +ng_minimize(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + + irs_irp_disconnect(pvt->girpdata); +} + + + + +/* Private */ + + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c b/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c new file mode 100644 index 0000000000..d032f6a608 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996,1998 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <fcntl.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <irs.h> +#include <irp.h> +#include <isc/irpmarshall.h> + +#include <isc/memcluster.h> +#include <isc/misc.h> + +#include "irs_p.h" +#include "lcl_p.h" +#include "irp_p.h" + +#include "port_after.h" + +#define MAXALIASES 35 +#define MAXADDRSIZE 4 + +struct pvt { + struct irp_p *girpdata; + int warned; + struct nwent net; +}; + +/* Forward */ + +static void nw_close(struct irs_nw *); +static struct nwent * nw_byname(struct irs_nw *, const char *, int); +static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int); +static struct nwent * nw_next(struct irs_nw *); +static void nw_rewind(struct irs_nw *); +static void nw_minimize(struct irs_nw *); + +static void free_nw(struct nwent *nw); + + +/* Public */ + +/*% + * struct irs_nw * irs_irp_nw(struct irs_acc *this) + * + */ + +struct irs_nw * +irs_irp_nw(struct irs_acc *this) { + struct irs_nw *nw; + struct pvt *pvt; + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + + if (!(nw = memget(sizeof *nw))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(nw, 0x0, sizeof *nw); + pvt->girpdata = this->private; + + nw->private = pvt; + nw->close = nw_close; + nw->byname = nw_byname; + nw->byaddr = nw_byaddr; + nw->next = nw_next; + nw->rewind = nw_rewind; + nw->minimize = nw_minimize; + return (nw); +} + +/* Methods */ + +/*% + * void nw_close(struct irs_nw *this) + * + */ + +static void +nw_close(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + nw_minimize(this); + + free_nw(&pvt->net); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +/*% + * struct nwent * nw_byaddr(struct irs_nw *this, void *net, + * int length, int type) + * + */ + +static struct nwent * +nw_byaddr(struct irs_nw *this, void *net, int length, int type) { + struct pvt *pvt = (struct pvt *)this->private; + struct nwent *nw = &pvt->net; + char *body = NULL; + size_t bodylen; + int code; + char paddr[24]; /*%< bigenough for ip4 w/ cidr spec. */ + char text[256]; + + if (inet_net_ntop(type, net, length, paddr, sizeof paddr) == NULL) { + return (NULL); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getnetbyaddr %s %s", + paddr, ADDR_T_STR(type)) != 0) + return (NULL); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETNET_OK) { + free_nw(nw); + if (irp_unmarshall_nw(nw, body) != 0) { + nw = NULL; + } + } else { + nw = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (nw); +} + +/*% + * struct nwent * nw_byname(struct irs_nw *this, const char *name, int type) + * + */ + +static struct nwent * +nw_byname(struct irs_nw *this, const char *name, int type) { + struct pvt *pvt = (struct pvt *)this->private; + struct nwent *nw = &pvt->net; + char *body = NULL; + size_t bodylen; + int code; + char text[256]; + + if (nw->n_name != NULL && + strcmp(name, nw->n_name) == 0 && + nw->n_addrtype == type) { + return (nw); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getnetbyname %s", name) != 0) + return (NULL); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETNET_OK) { + free_nw(nw); + if (irp_unmarshall_nw(nw, body) != 0) { + nw = NULL; + } + } else { + nw = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (nw); +} + +/*% + * void nw_rewind(struct irs_nw *this) + * + */ + +static void +nw_rewind(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + char text[256]; + int code; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return; + } + + if (irs_irp_send_command(pvt->girpdata, "setnetent") != 0) { + return; + } + + code = irs_irp_read_response(pvt->girpdata, text, sizeof text); + if (code != IRPD_GETNET_SETOK) { + if (irp_log_errors) { + syslog(LOG_WARNING, "setnetent failed: %s", text); + } + } + + return; +} + +/*% + * Prepares the cache if necessary and returns the first, or + * next item from it. + */ + +static struct nwent * +nw_next(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct nwent *nw = &pvt->net; + char *body; + size_t bodylen; + int code; + char text[256]; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getnetent") != 0) { + return (NULL); + } + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETNET_OK) { + free_nw(nw); + if (irp_unmarshall_nw(nw, body) != 0) { + nw = NULL; + } + } else { + nw = NULL; + } + + if (body != NULL) + memput(body, bodylen); + return (nw); +} + +/*% + * void nw_minimize(struct irs_nw *this) + * + */ + +static void +nw_minimize(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + irs_irp_disconnect(pvt->girpdata); +} + + + + +/* private. */ + +/*% + * deallocate all the memory irp_unmarshall_pw allocated. + * + */ + +static void +free_nw(struct nwent *nw) { + char **p; + + if (nw == NULL) + return; + + if (nw->n_name != NULL) + free(nw->n_name); + + if (nw->n_aliases != NULL) { + for (p = nw->n_aliases ; *p != NULL ; p++) { + free(*p); + } + free(nw->n_aliases); + } + + if (nw->n_addr != NULL) + free(nw->n_addr); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_p.h b/usr/src/lib/libresolv2_joy/common/irs/irp_p.h new file mode 100644 index 0000000000..4f943f81bd --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_p.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: irp_p.h,v 1.5 2005/04/27 04:56:28 sra Exp $ + */ + +#ifndef _IRP_P_H_INCLUDED +#define _IRP_P_H_INCLUDED + +#include <stdio.h> + +struct irp_p { + char inbuffer[1024]; + int inlast; /*%< index of one past the last char in buffer */ + int incurr; /*%< index of the next char to be read from buffer */ + int fdCxn; +}; + +/* + * Externs. + */ + +extern struct irs_acc * irs_irp_acc __P((const char *)); +extern struct irs_gr * irs_irp_gr __P((struct irs_acc *)); +extern struct irs_pw * irs_irp_pw __P((struct irs_acc *)); +extern struct irs_sv * irs_irp_sv __P((struct irs_acc *)); +extern struct irs_pr * irs_irp_pr __P((struct irs_acc *)); +extern struct irs_ho * irs_irp_ho __P((struct irs_acc *)); +extern struct irs_nw * irs_irp_nw __P((struct irs_acc *)); +extern struct irs_ng * irs_irp_ng __P((struct irs_acc *)); + +int irs_irp_connect(struct irp_p *pvt); +int irs_irp_is_connected(struct irp_p *pvt); +void irs_irp_disconnect(struct irp_p *pvt); +int irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen); +char *irs_irp_read_body(struct irp_p *pvt, size_t *size); +int irs_irp_get_full_response(struct irp_p *pvt, int *code, + char *text, size_t textlen, + char **body, size_t *bodylen); + +extern int irp_log_errors; + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c b/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c new file mode 100644 index 0000000000..a78f5b2b73 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <syslog.h> +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <netdb.h> +#include <syslog.h> + +#include <irs.h> +#include <irp.h> +#include <isc/memcluster.h> +#include <isc/irpmarshall.h> + +#include "irs_p.h" +#include "lcl_p.h" +#include "irp_p.h" + +#include "port_after.h" + + +#define MAXALIASES 35 + +/* Types */ + +struct pvt { + struct irp_p *girpdata; + int warned; + struct protoent proto; +}; + +/* Forward */ + +static void pr_close(struct irs_pr *); +static struct protoent * pr_next(struct irs_pr *); +static struct protoent * pr_byname(struct irs_pr *, const char *); +static struct protoent * pr_bynumber(struct irs_pr *, int); +static void pr_rewind(struct irs_pr *); +static void pr_minimize(struct irs_pr *); + +static void free_proto(struct protoent *pr); + +/* Public */ + +/*% + * struct irs_pr * irs_irp_pr(struct irs_acc *this) + * + */ + +struct irs_pr * +irs_irp_pr(struct irs_acc *this) { + struct irs_pr *pr; + struct pvt *pvt; + + if (!(pr = memget(sizeof *pr))) { + errno = ENOMEM; + return (NULL); + } + memset(pr, 0x0, sizeof *pr); + + if (!(pvt = memget(sizeof *pvt))) { + memput(pr, sizeof *pr); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->girpdata = this->private; + + pr->private = pvt; + pr->close = pr_close; + pr->byname = pr_byname; + pr->bynumber = pr_bynumber; + pr->next = pr_next; + pr->rewind = pr_rewind; + pr->minimize = pr_minimize; + return (pr); +} + +/* Methods */ + +/*% + * void pr_close(struct irs_pr *this) + * + */ + +static void +pr_close(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + pr_minimize(this); + + free_proto(&pvt->proto); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +/*% + * struct protoent * pr_byname(struct irs_pr *this, const char *name) + * + */ + +static struct protoent * +pr_byname(struct irs_pr *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + struct protoent *pr = &pvt->proto; + char *body = NULL; + size_t bodylen; + int code; + int i; + char text[256]; + + if (pr->p_name != NULL && strcmp(name, pr->p_name) == 0) { + return (pr); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + i = irs_irp_send_command(pvt->girpdata, "getprotobyname %s", name); + if (i != 0) + return (NULL); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETPROTO_OK) { + free_proto(pr); + if (irp_unmarshall_pr(pr, body) != 0) { + pr = NULL; + } + } else { + pr = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (pr); +} + +/*% + * struct protoent * pr_bynumber(struct irs_pr *this, int proto) + * + */ + +static struct protoent * +pr_bynumber(struct irs_pr *this, int proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct protoent *pr = &pvt->proto; + char *body = NULL; + size_t bodylen; + int code; + int i; + char text[256]; + + if (pr->p_name != NULL && proto == pr->p_proto) { + return (pr); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + i = irs_irp_send_command(pvt->girpdata, "getprotobynumber %d", proto); + if (i != 0) + return (NULL); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETPROTO_OK) { + free_proto(pr); + if (irp_unmarshall_pr(pr, body) != 0) { + pr = NULL; + } + } else { + pr = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (pr); +} + +/*% + * void pr_rewind(struct irs_pr *this) + * + */ + +static void +pr_rewind(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + char text[256]; + int code; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return; + } + + if (irs_irp_send_command(pvt->girpdata, "setprotoent") != 0) { + return; + } + + code = irs_irp_read_response(pvt->girpdata, text, sizeof text); + if (code != IRPD_GETPROTO_SETOK) { + if (irp_log_errors) { + syslog(LOG_WARNING, "setprotoent failed: %s", text); + } + } + + return; +} + +/*% + * Prepares the cache if necessary and returns the next item in it. + * + */ + +static struct protoent * +pr_next(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct protoent *pr = &pvt->proto; + char *body; + size_t bodylen; + int code; + char text[256]; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getprotoent") != 0) { + return (NULL); + } + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETPROTO_OK) { + free_proto(pr); + if (irp_unmarshall_pr(pr, body) != 0) { + pr = NULL; + } + } else { + pr = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (pr); +} + +/*% + * void pr_minimize(struct irs_pr *this) + * + */ + +static void +pr_minimize(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + irs_irp_disconnect(pvt->girpdata); +} + +/*% + * Deallocate all the memory irp_unmarshall_pr allocated. + * + */ + +static void +free_proto(struct protoent *pr) { + char **p; + + if (pr == NULL) + return; + + if (pr->p_name != NULL) + free(pr->p_name); + + for (p = pr->p_aliases ; p != NULL && *p != NULL ; p++) + free(*p); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c b/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c new file mode 100644 index 0000000000..5cd3e74032 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996,1998 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> + +#ifdef IRS_LCL_SV_DB +#include <db.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> + +#include <irs.h> +#include <irp.h> +#include <isc/irpmarshall.h> +#include <isc/memcluster.h> + +#include "irs_p.h" +#include "lcl_p.h" +#include "irp_p.h" + +#include "port_after.h" + +/* Types */ + +struct pvt { + struct irp_p *girpdata; + int warned; + struct servent service; +}; + +/* Forward */ + +static void sv_close(struct irs_sv*); +static struct servent * sv_next(struct irs_sv *); +static struct servent * sv_byname(struct irs_sv *, const char *, + const char *); +static struct servent * sv_byport(struct irs_sv *, int, const char *); +static void sv_rewind(struct irs_sv *); +static void sv_minimize(struct irs_sv *); + +static void free_service(struct servent *sv); + + + +/* Public */ + +/*% + * struct irs_sv * irs_irp_sv(struct irs_acc *this) + * + */ + +struct irs_sv * +irs_irp_sv(struct irs_acc *this) { + struct irs_sv *sv; + struct pvt *pvt; + + if ((sv = memget(sizeof *sv)) == NULL) { + errno = ENOMEM; + return (NULL); + } + memset(sv, 0x0, sizeof *sv); + + if ((pvt = memget(sizeof *pvt)) == NULL) { + memput(sv, sizeof *sv); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pvt->girpdata = this->private; + + sv->private = pvt; + sv->close = sv_close; + sv->next = sv_next; + sv->byname = sv_byname; + sv->byport = sv_byport; + sv->rewind = sv_rewind; + sv->minimize = sv_minimize; + + return (sv); +} + +/* Methods */ + +/*% + * void sv_close(struct irs_sv *this) + * + */ + +static void +sv_close(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + + sv_minimize(this); + + free_service(&pvt->service); + + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +/*% + * Fills the cache if necessary and returns the next item from it. + * + */ + +static struct servent * +sv_next(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct servent *sv = &pvt->service; + char *body; + size_t bodylen; + int code; + char text[256]; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getservent") != 0) { + return (NULL); + } + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETSERVICE_OK) { + free_service(sv); + if (irp_unmarshall_sv(sv, body) != 0) { + sv = NULL; + } + } else { + sv = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (sv); +} + +/*% + * struct servent * sv_byname(struct irs_sv *this, const char *name, + * const char *proto) + * + */ + +static struct servent * +sv_byname(struct irs_sv *this, const char *name, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct servent *sv = &pvt->service; + char *body; + char text[256]; + size_t bodylen; + int code; + + if (sv->s_name != NULL && + strcmp(name, sv->s_name) == 0 && + strcasecmp(proto, sv->s_proto) == 0) { + return (sv); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getservbyname %s %s", + name, proto) != 0) + return (NULL); + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETSERVICE_OK) { + free_service(sv); + if (irp_unmarshall_sv(sv, body) != 0) { + sv = NULL; + } + } else { + sv = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (sv); +} + +/*% + * struct servent * sv_byport(struct irs_sv *this, int port, + * const char *proto) + * + */ + +static struct servent * +sv_byport(struct irs_sv *this, int port, const char *proto) { + struct pvt *pvt = (struct pvt *)this->private; + struct servent *sv = &pvt->service; + char *body; + size_t bodylen; + char text[256]; + int code; + + if (sv->s_name != NULL && + port == sv->s_port && + strcasecmp(proto, sv->s_proto) == 0) { + return (sv); + } + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return (NULL); + } + + if (irs_irp_send_command(pvt->girpdata, "getservbyport %d %s", + ntohs((short)port), proto) != 0) { + return (NULL); + } + + if (irs_irp_get_full_response(pvt->girpdata, &code, + text, sizeof text, + &body, &bodylen) != 0) { + return (NULL); + } + + if (code == IRPD_GETSERVICE_OK) { + free_service(sv); + if (irp_unmarshall_sv(sv, body) != 0) { + sv = NULL; + } + } else { + sv = NULL; + } + + if (body != NULL) { + memput(body, bodylen); + } + + return (sv); +} + +/*% + * void sv_rewind(struct irs_sv *this) + * + */ + +static void +sv_rewind(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + char text[256]; + int code; + + if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) { + return; + } + + if (irs_irp_send_command(pvt->girpdata, "setservent") != 0) { + return; + } + + code = irs_irp_read_response(pvt->girpdata, text, sizeof text); + if (code != IRPD_GETSERVICE_SETOK) { + if (irp_log_errors) { + syslog(LOG_WARNING, "setservent failed: %s", text); + } + } + + return; +} + +/*% + * void sv_minimize(struct irs_sv *this) + * + */ + +static void +sv_minimize(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + + irs_irp_disconnect(pvt->girpdata); +} + + + + + + +static void +free_service(struct servent *sv) { + char **p; + + if (sv == NULL) { + return; + } + + if (sv->s_name != NULL) { + free(sv->s_name); + } + + for (p = sv->s_aliases ; p != NULL && *p != NULL ; p++) { + free(*p); + } + + if (sv->s_proto != NULL) { + free(sv->s_proto); + } +} + + + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c b/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c new file mode 100644 index 0000000000..a33bb5bfbd --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c @@ -0,0 +1,2297 @@ +/* + * Copyright(c) 1989, 1993, 1995 + * 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. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#if 0 + +Check values are in approrpriate endian order. + +Double check memory allocations on unmarhsalling + +#endif + + +/* Extern */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <stdio.h> +#include <ctype.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <utmp.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> + +#include <irs.h> +#include <isc/memcluster.h> +#include <isc/irpmarshall.h> + +#include "port_after.h" + + +#ifndef HAVE_STRNDUP +static char *strndup(const char *str, size_t len); +#endif + +static char **splitarray(const char *buffer, const char *buffend, char delim); +static int joinarray(char * const * argv, char *buffer, char delim); +static char *getfield(char **res, size_t reslen, char **buffer, char delim); +static size_t joinlength(char * const *argv); +static void free_array(char **argv, size_t entries); + +#define ADDR_T_STR(x) (x == AF_INET ? "AF_INET" :\ + (x == AF_INET6 ? "AF_INET6" : "UNKNOWN")) + +#define MAXPADDRSIZE (sizeof "255.255.255.255" + 1) + +static char COMMA = ','; + +static const char *COMMASTR = ","; +static const char *COLONSTR = ":"; + + + +/* See big comment at bottom of irpmarshall.h for description. */ + + +#ifdef WANT_IRS_PW +/* +++++++++++++++++++++++++ struct passwd +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on sucess, -1 on failure. + * + */ + +int +irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len) { + size_t need = 1 ; /*%< for null byte */ + char pwUid[24]; + char pwGid[24]; + char pwChange[24]; + char pwExpire[24]; + const char *pwClass; + const char *fieldsep = COLONSTR; + + if (pw == NULL || len == NULL) { + errno = EINVAL; + return (-1); + } + + sprintf(pwUid, "%ld", (long)pw->pw_uid); + sprintf(pwGid, "%ld", (long)pw->pw_gid); + +#ifdef HAVE_PW_CHANGE + sprintf(pwChange, "%ld", (long)pw->pw_change); +#else + pwChange[0] = '0'; + pwChange[1] = '\0'; +#endif + +#ifdef HAVE_PW_EXPIRE + sprintf(pwExpire, "%ld", (long)pw->pw_expire); +#else + pwExpire[0] = '0'; + pwExpire[1] = '\0'; +#endif + +#ifdef HAVE_PW_CLASS + pwClass = pw->pw_class; +#else + pwClass = ""; +#endif + + need += strlen(pw->pw_name) + 1; /*%< one for fieldsep */ + need += strlen(pw->pw_passwd) + 1; + need += strlen(pwUid) + 1; + need += strlen(pwGid) + 1; + need += strlen(pwClass) + 1; + need += strlen(pwChange) + 1; + need += strlen(pwExpire) + 1; + need += strlen(pw->pw_gecos) + 1; + need += strlen(pw->pw_dir) + 1; + need += strlen(pw->pw_shell) + 1; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + strcpy(*buffer, pw->pw_name); strcat(*buffer, fieldsep); + strcat(*buffer, pw->pw_passwd); strcat(*buffer, fieldsep); + strcat(*buffer, pwUid); strcat(*buffer, fieldsep); + strcat(*buffer, pwGid); strcat(*buffer, fieldsep); + strcat(*buffer, pwClass); strcat(*buffer, fieldsep); + strcat(*buffer, pwChange); strcat(*buffer, fieldsep); + strcat(*buffer, pwExpire); strcat(*buffer, fieldsep); + strcat(*buffer, pw->pw_gecos); strcat(*buffer, fieldsep); + strcat(*buffer, pw->pw_dir); strcat(*buffer, fieldsep); + strcat(*buffer, pw->pw_shell); strcat(*buffer, fieldsep); + + return (0); +} + +/*% + * int irp_unmarshall_pw(struct passwd *pw, char *buffer) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on success, -1 on failure + * + */ + +int +irp_unmarshall_pw(struct passwd *pw, char *buffer) { + char *name, *pass, *class, *gecos, *dir, *shell; + uid_t pwuid; + gid_t pwgid; + time_t pwchange; + time_t pwexpire; + char *p; + long t; + char tmpbuf[24]; + char *tb = &tmpbuf[0]; + char fieldsep = ':'; + int myerrno = EINVAL; + + name = pass = class = gecos = dir = shell = NULL; + p = buffer; + + /* pw_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0) { + goto error; + } + + /* pw_passwd field */ + pass = NULL; + if (getfield(&pass, 0, &p, fieldsep) == NULL) { /*%< field can be empty */ + goto error; + } + + + /* pw_uid field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + pwuid = (uid_t)t; + if ((long) pwuid != t) { /*%< value must have been too big. */ + goto error; + } + + + + /* pw_gid field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + pwgid = (gid_t)t; + if ((long)pwgid != t) { /*%< value must have been too big. */ + goto error; + } + + + + /* pw_class field */ + class = NULL; + if (getfield(&class, 0, &p, fieldsep) == NULL) { + goto error; + } + + + + /* pw_change field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + pwchange = (time_t)t; + if ((long)pwchange != t) { /*%< value must have been too big. */ + goto error; + } + + + + /* pw_expire field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + pwexpire = (time_t)t; + if ((long) pwexpire != t) { /*%< value must have been too big. */ + goto error; + } + + + + /* pw_gecos field */ + gecos = NULL; + if (getfield(&gecos, 0, &p, fieldsep) == NULL) { + goto error; + } + + + + /* pw_dir field */ + dir = NULL; + if (getfield(&dir, 0, &p, fieldsep) == NULL) { + goto error; + } + + + + /* pw_shell field */ + shell = NULL; + if (getfield(&shell, 0, &p, fieldsep) == NULL) { + goto error; + } + + + + pw->pw_name = name; + pw->pw_passwd = pass; + pw->pw_uid = pwuid; + pw->pw_gid = pwgid; + pw->pw_gecos = gecos; + pw->pw_dir = dir; + pw->pw_shell = shell; + +#ifdef HAVE_PW_CHANGE + pw->pw_change = pwchange; +#endif +#ifdef HAVE_PW_CLASS + pw->pw_class = class; +#endif +#ifdef HAVE_PW_EXPIRE + pw->pw_expire = pwexpire; +#endif + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + if (pass != NULL) free(pass); + if (gecos != NULL) free(gecos); + if (dir != NULL) free(dir); + if (shell != NULL) free(shell); + + return (-1); +} + +/* ------------------------- struct passwd ------------------------- */ +#endif /* WANT_IRS_PW */ +/* +++++++++++++++++++++++++ struct group +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_gr(const struct group *gr, char **buffer, size_t *len) + * + * notes: \li + * + * See irpmarshall.h. + * + * return: \li + * + * 0 on success, -1 on failure + */ + +int +irp_marshall_gr(const struct group *gr, char **buffer, size_t *len) { + size_t need = 1; /*%< for null byte */ + char grGid[24]; + const char *fieldsep = COLONSTR; + + if (gr == NULL || len == NULL) { + errno = EINVAL; + return (-1); + } + + sprintf(grGid, "%ld", (long)gr->gr_gid); + + need += strlen(gr->gr_name) + 1; +#ifndef MISSING_GR_PASSWD + need += strlen(gr->gr_passwd) + 1; +#else + need++; +#endif + need += strlen(grGid) + 1; + need += joinlength(gr->gr_mem) + 1; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + strcpy(*buffer, gr->gr_name); strcat(*buffer, fieldsep); +#ifndef MISSING_GR_PASSWD + strcat(*buffer, gr->gr_passwd); +#endif + strcat(*buffer, fieldsep); + strcat(*buffer, grGid); strcat(*buffer, fieldsep); + joinarray(gr->gr_mem, *buffer, COMMA) ; strcat(*buffer, fieldsep); + + return (0); +} + +/*% + * int irp_unmarshall_gr(struct group *gr, char *buffer) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on success and -1 on failure. + * + */ + +int +irp_unmarshall_gr(struct group *gr, char *buffer) { + char *p, *q; + gid_t grgid; + long t; + char *name = NULL; + char *pass = NULL; + char **members = NULL; + char tmpbuf[24]; + char *tb; + char fieldsep = ':'; + int myerrno = EINVAL; + + if (gr == NULL || buffer == NULL) { + errno = EINVAL; + return (-1); + } + + p = buffer; + + /* gr_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) { + goto error; + } + + + /* gr_passwd field */ + pass = NULL; + if (getfield(&pass, 0, &p, fieldsep) == NULL) { + goto error; + } + + + /* gr_gid field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + grgid = (gid_t)t; + if ((long) grgid != t) { /*%< value must have been too big. */ + goto error; + } + + + /* gr_mem field. Member names are separated by commas */ + q = strchr(p, fieldsep); + if (q == NULL) { + goto error; + } + members = splitarray(p, q, COMMA); + if (members == NULL) { + myerrno = errno; + goto error; + } + p = q + 1; + + + gr->gr_name = name; +#ifndef MISSING_GR_PASSWD + gr->gr_passwd = pass; +#endif + gr->gr_gid = grgid; + gr->gr_mem = members; + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + if (pass != NULL) free(pass); + + return (-1); +} + + +/* ------------------------- struct group ------------------------- */ + + + + +/* +++++++++++++++++++++++++ struct servent +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on success, -1 on failure. + * + */ + +int +irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len) { + size_t need = 1; /*%< for null byte */ + char svPort[24]; + const char *fieldsep = COLONSTR; + short realport; + + if (sv == NULL || len == NULL) { + errno = EINVAL; + return (-1); + } + + /* the int s_port field is actually a short in network order. We + want host order to make the marshalled data look correct */ + realport = ntohs((short)sv->s_port); + sprintf(svPort, "%d", realport); + + need += strlen(sv->s_name) + 1; + need += joinlength(sv->s_aliases) + 1; + need += strlen(svPort) + 1; + need += strlen(sv->s_proto) + 1; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + strcpy(*buffer, sv->s_name); strcat(*buffer, fieldsep); + joinarray(sv->s_aliases, *buffer, COMMA); strcat(*buffer, fieldsep); + strcat(*buffer, svPort); strcat(*buffer, fieldsep); + strcat(*buffer, sv->s_proto); strcat(*buffer, fieldsep); + + return (0); +} + +/*% + * int irp_unmarshall_sv(struct servent *sv, char *buffer) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on success, -1 on failure. + * + */ + +int +irp_unmarshall_sv(struct servent *sv, char *buffer) { + char *p, *q; + short svport; + long t; + char *name = NULL; + char *proto = NULL; + char **aliases = NULL; + char tmpbuf[24]; + char *tb; + char fieldsep = ':'; + int myerrno = EINVAL; + + if (sv == NULL || buffer == NULL) + return (-1); + + p = buffer; + + + /* s_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) { + goto error; + } + + + /* s_aliases field */ + q = strchr(p, fieldsep); + if (q == NULL) { + goto error; + } + aliases = splitarray(p, q, COMMA); + if (aliases == NULL) { + myerrno = errno; + goto error; + } + p = q + 1; + + + /* s_port field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + svport = (short)t; + if ((long) svport != t) { /*%< value must have been too big. */ + goto error; + } + svport = htons(svport); + + /* s_proto field */ + proto = NULL; + if (getfield(&proto, 0, &p, fieldsep) == NULL) { + goto error; + } + + sv->s_name = name; + sv->s_aliases = aliases; + sv->s_port = svport; + sv->s_proto = proto; + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + if (proto != NULL) free(proto); + free_array(aliases, 0); + + return (-1); +} + + +/* ------------------------- struct servent ------------------------- */ + +/* +++++++++++++++++++++++++ struct protoent +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on success and -1 on failure. + * + */ + +int +irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len) { + size_t need = 1; /*%< for null byte */ + char prProto[24]; + const char *fieldsep = COLONSTR; + + if (pr == NULL || len == NULL) { + errno = EINVAL; + return (-1); + } + + sprintf(prProto, "%d", (int)pr->p_proto); + + need += strlen(pr->p_name) + 1; + need += joinlength(pr->p_aliases) + 1; + need += strlen(prProto) + 1; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + strcpy(*buffer, pr->p_name); strcat(*buffer, fieldsep); + joinarray(pr->p_aliases, *buffer, COMMA); strcat(*buffer, fieldsep); + strcat(*buffer, prProto); strcat(*buffer, fieldsep); + + return (0); + +} + +/*% + * int irp_unmarshall_pr(struct protoent *pr, char *buffer) + * + * notes: \li + * + * See irpmarshall.h + * + * return: \li + * + * 0 on success, -1 on failure + * + */ + +int irp_unmarshall_pr(struct protoent *pr, char *buffer) { + char *p, *q; + int prproto; + long t; + char *name = NULL; + char **aliases = NULL; + char tmpbuf[24]; + char *tb; + char fieldsep = ':'; + int myerrno = EINVAL; + + if (pr == NULL || buffer == NULL) { + errno = EINVAL; + return (-1); + } + + p = buffer; + + /* p_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) { + goto error; + } + + + /* p_aliases field */ + q = strchr(p, fieldsep); + if (q == NULL) { + goto error; + } + aliases = splitarray(p, q, COMMA); + if (aliases == NULL) { + myerrno = errno; + goto error; + } + p = q + 1; + + + /* p_proto field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + prproto = (int)t; + if ((long) prproto != t) { /*%< value must have been too big. */ + goto error; + } + + pr->p_name = name; + pr->p_aliases = aliases; + pr->p_proto = prproto; + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + free_array(aliases, 0); + + return (-1); +} + +/* ------------------------- struct protoent ------------------------- */ + + + +/* +++++++++++++++++++++++++ struct hostent +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len) + * + * notes: \li + * + * See irpmarshall.h. + * + * return: \li + * + * 0 on success, -1 on failure. + * + */ + +int +irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len) { + size_t need = 1; /*%< for null byte */ + char hoaddrtype[24]; + char holength[24]; + char **av; + char *p; + int addrlen; + int malloced = 0; + size_t remlen; + const char *fieldsep = "@"; + + if (ho == NULL || len == NULL) { + errno = EINVAL; + return (-1); + } + + switch(ho->h_addrtype) { + case AF_INET: + strcpy(hoaddrtype, "AF_INET"); + break; + + case AF_INET6: + strcpy(hoaddrtype, "AF_INET6"); + break; + + default: + errno = EINVAL; + return (-1); + } + + sprintf(holength, "%d", ho->h_length); + + need += strlen(ho->h_name) + 1; + need += joinlength(ho->h_aliases) + 1; + need += strlen(hoaddrtype) + 1; + need += strlen(holength) + 1; + + /* we determine an upper bound on the string length needed, not an + exact length. */ + addrlen = (ho->h_addrtype == AF_INET ? 16 : 46) ; /*%< XX other AF's?? */ + for (av = ho->h_addr_list; av != NULL && *av != NULL ; av++) + need += addrlen; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + malloced = 1; + } + + strcpy(*buffer, ho->h_name); strcat(*buffer, fieldsep); + joinarray(ho->h_aliases, *buffer, COMMA); strcat(*buffer, fieldsep); + strcat(*buffer, hoaddrtype); strcat(*buffer, fieldsep); + strcat(*buffer, holength); strcat(*buffer, fieldsep); + + p = *buffer + strlen(*buffer); + remlen = need - strlen(*buffer); + for (av = ho->h_addr_list ; av != NULL && *av != NULL ; av++) { + if (inet_ntop(ho->h_addrtype, *av, p, remlen) == NULL) { + goto error; + } + if (*(av + 1) != NULL) + strcat(p, COMMASTR); + remlen -= strlen(p); + p += strlen(p); + } + strcat(*buffer, fieldsep); + + return (0); + + error: + if (malloced) { + memput(*buffer, need); + } + + return (-1); +} + +/*% + * int irp_unmarshall_ho(struct hostent *ho, char *buffer) + * + * notes: \li + * + * See irpmarshall.h. + * + * return: \li + * + * 0 on success, -1 on failure. + * + */ + +int +irp_unmarshall_ho(struct hostent *ho, char *buffer) { + char *p, *q, *r; + int hoaddrtype; + int holength; + long t; + char *name; + char **aliases = NULL; + char **hohaddrlist = NULL; + size_t hoaddrsize; + char tmpbuf[24]; + char *tb; + char **alist; + int addrcount; + char fieldsep = '@'; + int myerrno = EINVAL; + + if (ho == NULL || buffer == NULL) { + errno = EINVAL; + return (-1); + } + + p = buffer; + + /* h_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) { + goto error; + } + + + /* h_aliases field */ + q = strchr(p, fieldsep); + if (q == NULL) { + goto error; + } + aliases = splitarray(p, q, COMMA); + if (aliases == NULL) { + myerrno = errno; + goto error; + } + p = q + 1; + + + /* h_addrtype field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + if (strcmp(tmpbuf, "AF_INET") == 0) + hoaddrtype = AF_INET; + else if (strcmp(tmpbuf, "AF_INET6") == 0) + hoaddrtype = AF_INET6; + else + goto error; + + + /* h_length field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + t = strtol(tmpbuf, &tb, 10); + if (*tb) { + goto error; /*%< junk in value */ + } + holength = (int)t; + if ((long) holength != t) { /*%< value must have been too big. */ + goto error; + } + + + /* h_addr_list field */ + q = strchr(p, fieldsep); + if (q == NULL) + goto error; + + /* count how many addresss are in there */ + if (q > p + 1) { + for (addrcount = 1, r = p ; r != q ; r++) { + if (*r == COMMA) + addrcount++; + } + } else { + addrcount = 0; + } + + hoaddrsize = (addrcount + 1) * sizeof (char *); + hohaddrlist = malloc(hoaddrsize); + if (hohaddrlist == NULL) { + myerrno = ENOMEM; + goto error; + } + + memset(hohaddrlist, 0x0, hoaddrsize); + + alist = hohaddrlist; + for (t = 0, r = p ; r != q ; p = r + 1, t++) { + char saved; + while (r != q && *r != COMMA) r++; + saved = *r; + *r = 0x0; + + alist[t] = malloc(hoaddrtype == AF_INET ? 4 : 16); + if (alist[t] == NULL) { + myerrno = ENOMEM; + goto error; + } + + if (inet_pton(hoaddrtype, p, alist[t]) == -1) + goto error; + *r = saved; + } + alist[t] = NULL; + + ho->h_name = name; + ho->h_aliases = aliases; + ho->h_addrtype = hoaddrtype; + ho->h_length = holength; + ho->h_addr_list = hohaddrlist; + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + free_array(hohaddrlist, 0); + free_array(aliases, 0); + + return (-1); +} + +/* ------------------------- struct hostent------------------------- */ + + + +/* +++++++++++++++++++++++++ struct netgrp +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_ng(const char *host, const char *user, + * const char *domain, char *buffer, size_t *len) + * + * notes: \li + * + * See note for irp_marshall_ng_start + * + * return: \li + * + * 0 on success, 0 on failure. + * + */ + +int +irp_marshall_ng(const char *host, const char *user, const char *domain, + char **buffer, size_t *len) { + size_t need = 1; /*%< for nul byte */ + const char *fieldsep = ","; + + if (len == NULL) { + errno = EINVAL; + return (-1); + } + + need += 4; /*%< two parens and two commas */ + need += (host == NULL ? 0 : strlen(host)); + need += (user == NULL ? 0 : strlen(user)); + need += (domain == NULL ? 0 : strlen(domain)); + + if (buffer == NULL) { + *len = need; + return (0); + } else if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + (*buffer)[0] = '('; + (*buffer)[1] = '\0'; + + if (host != NULL) + strcat(*buffer, host); + strcat(*buffer, fieldsep); + + if (user != NULL) + strcat(*buffer, user); + strcat(*buffer, fieldsep); + + if (domain != NULL) + strcat(*buffer, domain); + strcat(*buffer, ")"); + + return (0); +} + + + +/* ---------- */ + +/*% + * int irp_unmarshall_ng(const char **host, const char **user, + * const char **domain, char *buffer) + * + * notes: \li + * + * Unpacks the BUFFER into 3 character arrays it allocates and assigns + * to *HOST, *USER and *DOMAIN. If any field of the value is empty, + * then the corresponding paramater value will be set to NULL. + * + * return: \li + * + * 0 on success and -1 on failure. + */ + +int +irp_unmarshall_ng(const char **hostp, const char **userp, const char **domainp, + char *buffer) +{ + char *p, *q; + char fieldsep = ','; + int myerrno = EINVAL; + char *host, *user, *domain; + + if (userp == NULL || hostp == NULL || + domainp == NULL || buffer == NULL) { + errno = EINVAL; + return (-1); + } + + host = user = domain = NULL; + + p = buffer; + while (isspace((unsigned char)*p)) { + p++; + } + if (*p != '(') { + goto error; + } + + q = p + 1; + while (*q && *q != fieldsep) + q++; + if (!*q) { + goto error; + } else if (q > p + 1) { + host = strndup(p, q - p); + } + + p = q + 1; + if (!*p) { + goto error; + } else if (*p != fieldsep) { + q = p + 1; + while (*q && *q != fieldsep) + q++; + if (!*q) { + goto error; + } + user = strndup(p, q - p); + } else { + p++; + } + + if (!*p) { + goto error; + } else if (*p != ')') { + q = p + 1; + while (*q && *q != ')') + q++; + if (!*q) { + goto error; + } + domain = strndup(p, q - p); + } + *hostp = host; + *userp = user; + *domainp = domain; + + return (0); + + error: + errno = myerrno; + + if (host != NULL) free(host); + if (user != NULL) free(user); + + return (-1); +} + +/* ------------------------- struct netgrp ------------------------- */ + + + + +/* +++++++++++++++++++++++++ struct nwent +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len) + * + * notes: \li + * + * See at top. + * + * return: \li + * + * 0 on success and -1 on failure. + * + */ + +int +irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len) { + size_t need = 1; /*%< for null byte */ + char nAddrType[24]; + char nNet[MAXPADDRSIZE]; + const char *fieldsep = COLONSTR; + + if (ne == NULL || len == NULL) { + return (-1); + } + + strcpy(nAddrType, ADDR_T_STR(ne->n_addrtype)); + + if (inet_net_ntop(ne->n_addrtype, ne->n_addr, ne->n_length, + nNet, sizeof nNet) == NULL) { + return (-1); + } + + + need += strlen(ne->n_name) + 1; + need += joinlength(ne->n_aliases) + 1; + need += strlen(nAddrType) + 1; + need += strlen(nNet) + 1; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + strcpy(*buffer, ne->n_name); strcat(*buffer, fieldsep); + joinarray(ne->n_aliases, *buffer, COMMA) ; strcat(*buffer, fieldsep); + strcat(*buffer, nAddrType); strcat(*buffer, fieldsep); + strcat(*buffer, nNet); strcat(*buffer, fieldsep); + + return (0); +} + +/*% + * int irp_unmarshall_nw(struct nwent *ne, char *buffer) + * + * notes: \li + * + * See note up top. + * + * return: \li + * + * 0 on success and -1 on failure. + * + */ + +int +irp_unmarshall_nw(struct nwent *ne, char *buffer) { + char *p, *q; + int naddrtype; + long nnet; + int bits; + char *name = NULL; + char **aliases = NULL; + char tmpbuf[24]; + char *tb; + char fieldsep = ':'; + int myerrno = EINVAL; + + if (ne == NULL || buffer == NULL) { + goto error; + } + + p = buffer; + + /* n_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) { + goto error; + } + + + /* n_aliases field. Aliases are separated by commas */ + q = strchr(p, fieldsep); + if (q == NULL) { + goto error; + } + aliases = splitarray(p, q, COMMA); + if (aliases == NULL) { + myerrno = errno; + goto error; + } + p = q + 1; + + + /* h_addrtype field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + if (strcmp(tmpbuf, "AF_INET") == 0) + naddrtype = AF_INET; + else if (strcmp(tmpbuf, "AF_INET6") == 0) + naddrtype = AF_INET6; + else + goto error; + + + /* n_net field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + nnet = 0; + bits = inet_net_pton(naddrtype, tmpbuf, &nnet, sizeof nnet); + if (bits < 0) { + goto error; + } + + /* nnet = ntohl(nnet); */ /* keep in network order for nwent */ + + ne->n_name = name; + ne->n_aliases = aliases; + ne->n_addrtype = naddrtype; + ne->n_length = bits; + ne->n_addr = malloc(sizeof nnet); + if (ne->n_addr == NULL) { + goto error; + } + + memcpy(ne->n_addr, &nnet, sizeof nnet); + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + free_array(aliases, 0); + + return (-1); +} + + +/* ------------------------- struct nwent ------------------------- */ + + +/* +++++++++++++++++++++++++ struct netent +++++++++++++++++++++++++ */ + +/*% + * int irp_marshall_ne(struct netent *ne, char **buffer, size_t *len) + * + * notes: \li + * + * See at top. + * + * return: \li + * + * 0 on success and -1 on failure. + * + */ + +int +irp_marshall_ne(struct netent *ne, char **buffer, size_t *len) { + size_t need = 1; /*%< for null byte */ + char nAddrType[24]; + char nNet[MAXPADDRSIZE]; + const char *fieldsep = COLONSTR; + long nval; + + if (ne == NULL || len == NULL) { + return (-1); + } + + strcpy(nAddrType, ADDR_T_STR(ne->n_addrtype)); + + nval = htonl(ne->n_net); + if (inet_ntop(ne->n_addrtype, &nval, nNet, sizeof nNet) == NULL) { + return (-1); + } + + need += strlen(ne->n_name) + 1; + need += joinlength(ne->n_aliases) + 1; + need += strlen(nAddrType) + 1; + need += strlen(nNet) + 1; + + if (buffer == NULL) { + *len = need; + return (0); + } + + if (*buffer != NULL && need > *len) { + errno = EINVAL; + return (-1); + } + + if (*buffer == NULL) { + need += 2; /*%< for CRLF */ + *buffer = memget(need); + if (*buffer == NULL) { + errno = ENOMEM; + return (-1); + } + + *len = need; + } + + strcpy(*buffer, ne->n_name); strcat(*buffer, fieldsep); + joinarray(ne->n_aliases, *buffer, COMMA) ; strcat(*buffer, fieldsep); + strcat(*buffer, nAddrType); strcat(*buffer, fieldsep); + strcat(*buffer, nNet); strcat(*buffer, fieldsep); + + return (0); +} + +/*% + * int irp_unmarshall_ne(struct netent *ne, char *buffer) + * + * notes: \li + * + * See note up top. + * + * return: \li + * + * 0 on success and -1 on failure. + * + */ + +int +irp_unmarshall_ne(struct netent *ne, char *buffer) { + char *p, *q; + int naddrtype; + long nnet; + int bits; + char *name = NULL; + char **aliases = NULL; + char tmpbuf[24]; + char *tb; + char fieldsep = ':'; + int myerrno = EINVAL; + + if (ne == NULL || buffer == NULL) { + goto error; + } + + p = buffer; + + /* n_name field */ + name = NULL; + if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) { + goto error; + } + + + /* n_aliases field. Aliases are separated by commas */ + q = strchr(p, fieldsep); + if (q == NULL) { + goto error; + } + aliases = splitarray(p, q, COMMA); + if (aliases == NULL) { + myerrno = errno; + goto error; + } + p = q + 1; + + + /* h_addrtype field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + if (strcmp(tmpbuf, "AF_INET") == 0) + naddrtype = AF_INET; + else if (strcmp(tmpbuf, "AF_INET6") == 0) + naddrtype = AF_INET6; + else + goto error; + + + /* n_net field */ + tb = tmpbuf; + if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL || + strlen(tb) == 0U) { + goto error; + } + bits = inet_net_pton(naddrtype, tmpbuf, &nnet, sizeof nnet); + if (bits < 0) { + goto error; + } + nnet = ntohl(nnet); + + ne->n_name = name; + ne->n_aliases = aliases; + ne->n_addrtype = naddrtype; + ne->n_net = nnet; + + return (0); + + error: + errno = myerrno; + + if (name != NULL) free(name); + free_array(aliases, 0); + + return (-1); +} + + +/* ------------------------- struct netent ------------------------- */ + + +/* =========================================================================== */ + +/*% + * static char ** splitarray(const char *buffer, const char *buffend, char delim) + * + * notes: \li + * + * Split a delim separated astring. Not allowed + * to have two delims next to each other. BUFFER points to begining of + * string, BUFFEND points to one past the end of the string + * (i.e. points at where the null byte would be if null + * terminated). + * + * return: \li + * + * Returns a malloced array of pointers, each pointer pointing to a + * malloced string. If BUFEER is an empty string, then return values is + * array of 1 pointer that is NULL. Returns NULL on failure. + * + */ + +static char ** +splitarray(const char *buffer, const char *buffend, char delim) { + const char *p, *q; + int count = 0; + char **arr = NULL; + char **aptr; + + if (buffend < buffer) + return (NULL); + else if (buffend > buffer && *buffer == delim) + return (NULL); + else if (buffend > buffer && *(buffend - 1) == delim) + return (NULL); + + /* count the number of field and make sure none are empty */ + if (buffend > buffer + 1) { + for (count = 1, q = buffer ; q != buffend ; q++) { + if (*q == delim) { + if (q > buffer && (*(q - 1) == delim)) { + errno = EINVAL; + return (NULL); + } + count++; + } + } + } + + if (count > 0) { + count++ ; /*%< for NULL at end */ + aptr = arr = malloc(count * sizeof (char *)); + if (aptr == NULL) { + errno = ENOMEM; + return (NULL); + } + + memset(arr, 0x0, count * sizeof (char *)); + for (p = buffer ; p < buffend ; p++) { + for (q = p ; *q != delim && q != buffend ; q++) + /* nothing */; + *aptr = strndup(p, q - p); + + p = q; + aptr++; + } + *aptr = NULL; + } else { + arr = malloc(sizeof (char *)); + if (arr == NULL) { + errno = ENOMEM; + return (NULL); + } + + *arr = NULL; + } + + return (arr); +} + +/*% + * static size_t joinlength(char * const *argv) + * + * return: \li + * + * the number of bytes in all the arrays pointed at + * by argv, including their null bytes(which will usually be turned + * into commas). + * + * + */ + +static size_t +joinlength(char * const *argv) { + int len = 0; + + while (argv && *argv) { + len += (strlen(*argv) + 1); + argv++; + } + + return (len); +} + +/*% + * int joinarray(char * const *argv, char *buffer, char delim) + * + * notes: \li + * + * Copy all the ARGV strings into the end of BUFFER + * separating them with DELIM. BUFFER is assumed to have + * enough space to hold everything and to be already null-terminated. + * + * return: \li + * + * 0 unless argv or buffer is NULL. + * + * + */ + +static int +joinarray(char * const *argv, char *buffer, char delim) { + char * const *p; + char sep[2]; + + if (argv == NULL || buffer == NULL) { + errno = EINVAL; + return (-1); + } + + sep[0] = delim; + sep[1] = 0x0; + + for (p = argv ; *p != NULL ; p++) { + strcat(buffer, *p); + if (*(p + 1) != NULL) { + strcat(buffer, sep); + } + } + + return (0); +} + +/*% + * static char * getfield(char **res, size_t reslen, char **ptr, char delim) + * + * notes: \li + * + * Stores in *RES, which is a buffer of length RESLEN, a + * copy of the bytes from *PTR up to and including the first + * instance of DELIM. If *RES is NULL, then it will be + * assigned a malloced buffer to hold the copy. *PTR is + * modified to point at the found delimiter. + * + * return: \li + * + * If there was no delimiter, then NULL is returned, + * otherewise *RES is returned. + * + */ + +static char * +getfield(char **res, size_t reslen, char **ptr, char delim) { + char *q; + + if (res == NULL || ptr == NULL || *ptr == NULL) { + errno = EINVAL; + return (NULL); + } + + q = strchr(*ptr, delim); + + if (q == NULL) { + errno = EINVAL; + return (NULL); + } else { + if (*res == NULL) { + *res = strndup(*ptr, q - *ptr); + } else { + if ((size_t)(q - *ptr + 1) > reslen) { /*%< to big for res */ + errno = EINVAL; + return (NULL); + } else { + strncpy(*res, *ptr, q - *ptr); + (*res)[q - *ptr] = 0x0; + } + } + *ptr = q + 1; + } + + return (*res); +} + + + + + +#ifndef HAVE_STRNDUP +/* + * static char * strndup(const char *str, size_t len) + * + * notes: \li + * + * like strdup, except do len bytes instead of the whole string. Always + * null-terminates. + * + * return: \li + * + * The newly malloced string. + * + */ + +static char * +strndup(const char *str, size_t len) { + char *p = malloc(len + 1); + + if (p == NULL) + return (NULL); + strncpy(p, str, len); + p[len] = 0x0; + return (p); +} +#endif + +#if WANT_MAIN + +/*% + * static int strcmp_nws(const char *a, const char *b) + * + * notes: \li + * + * do a strcmp, except uneven lengths of whitespace compare the same + * + * return: \li + * + */ + +static int +strcmp_nws(const char *a, const char *b) { + while (*a && *b) { + if (isspace(*a) && isspace(*b)) { + do { + a++; + } while (isspace(*a)); + do { + b++; + } while (isspace(*b)); + } + if (*a < *b) + return (-1); + else if (*a > *b) + return (1); + + a++; + b++;; + } + + if (*a == *b) + return (0); + else if (*a > *b) + return (1); + else + return (-1); +} + +#endif + +/*% + * static void free_array(char **argv, size_t entries) + * + * notes: \li + * + * Free argv and each of the pointers inside it. The end of + * the array is when a NULL pointer is found inside. If + * entries is > 0, then NULL pointers inside the array do + * not indicate the end of the array. + * + */ + +static void +free_array(char **argv, size_t entries) { + char **p = argv; + int useEntries = (entries > 0U); + + if (argv == NULL) + return; + + while ((useEntries && entries > 0U) || *p) { + if (*p) + free(*p); + p++; + if (useEntries) + entries--; + } + free(argv); +} + + + + + +/* ************************************************** */ + +#if WANT_MAIN + +/*% takes an option to indicate what sort of marshalling(read the code) and + an argument. If the argument looks like a marshalled buffer(has a ':' + embedded) then it's unmarshalled and the remarshalled and the new string + is compared to the old one. +*/ + +int +main(int argc, char **argv) { + char buffer[1024]; + char *b = &buffer[0]; + size_t len = sizeof buffer; + char option; + + if (argc < 2 || argv[1][0] != '-') + exit(1); + + option = argv[1][1]; + argv++; + argc--; + + +#if 0 + { + char buff[10]; + char *p = argv[1], *q = &buff[0]; + + while (getfield(&q, sizeof buff, &p, ':') != NULL) { + printf("field: \"%s\"\n", q); + p++; + } + printf("p is now \"%s\"\n", p); + } +#endif + +#if 0 + { + char **x = splitarray(argv[1], argv[1] + strlen(argv[1]), + argv[2][0]); + char **p; + + if (x == NULL) + printf("split failed\n"); + + for (p = x ; p != NULL && *p != NULL ; p++) { + printf("\"%s\"\n", *p); + } + } +#endif + +#if 1 + switch(option) { + case 'n': { + struct nwent ne; + int i; + + if (strchr(argv[1], ':') != NULL) { + if (irp_unmarshall_nw(&ne, argv[1]) != 0) { + printf("Unmarhsalling failed\n"); + exit(1); + } + + printf("Name: \"%s\"\n", ne.n_name); + printf("Aliases:"); + for (i = 0 ; ne.n_aliases[i] != NULL ; i++) + printf("\n\t\"%s\"", ne.n_aliases[i]); + printf("\nAddrtype: %s\n", ADDR_T_STR(ne.n_addrtype)); + inet_net_ntop(ne.n_addrtype, ne.n_addr, ne.n_length, + buffer, sizeof buffer); + printf("Net: \"%s\"\n", buffer); + *((long*)ne.n_addr) = htonl(*((long*)ne.n_addr)); + inet_net_ntop(ne.n_addrtype, ne.n_addr, ne.n_length, + buffer, sizeof buffer); + printf("Corrected Net: \"%s\"\n", buffer); + } else { + struct netent *np1 = getnetbyname(argv[1]); + ne.n_name = np1->n_name; + ne.n_aliases = np1->n_aliases; + ne.n_addrtype = np1->n_addrtype; + ne.n_addr = &np1->n_net; + ne.n_length = (IN_CLASSA(np1->n_net) ? + 8 : + (IN_CLASSB(np1->n_net) ? + 16 : + (IN_CLASSC(np1->n_net) ? + 24 : -1))); + np1->n_net = htonl(np1->n_net); + if (irp_marshall_nw(&ne, &b, &len) != 0) { + printf("Marshalling failed\n"); + } + printf("%s\n", b); + } + break; + } + + + case 'r': { + char **hosts, **users, **domains; + size_t entries; + int i; + char *buff; + size_t size; + char *ngname; + + if (strchr(argv[1], '(') != NULL) { + if (irp_unmarshall_ng(&ngname, &entries, + &hosts, &users, &domains, + argv[1]) != 0) { + printf("unmarshall failed\n"); + exit(1); + } + +#define STRVAL(x) (x == NULL ? "*" : x) + + printf("%s {\n", ngname); + for (i = 0 ; i < entries ; i++) + printf("\t\"%s\" : \"%s\" : \"%s\"\n", + STRVAL(hosts[i]), + STRVAL(users[i]), + STRVAL(domains[i])); + printf("}\n\n\n"); + + + irp_marshall_ng_start(ngname, NULL, &size); + for (i = 0 ; i < entries ; i++) + irp_marshall_ng_next(hosts[i], users[i], + domains[i], NULL, &size); + irp_marshall_ng_end(NULL, &size); + + buff = malloc(size); + + irp_marshall_ng_start(ngname, buff, &size); + for (i = 0 ; i < entries ; i++) { + if (irp_marshall_ng_next(hosts[i], users[i], + domains[i], buff, + &size) != 0) + printf("next marshalling failed.\n"); + } + irp_marshall_ng_end(buff, &size); + + if (strcmp_nws(argv[1], buff) != 0) { + printf("compare failed:\n\t%s\n\t%s\n", + buffer, argv[1]); + } else { + printf("compare ok\n"); + } + } else { + char *h, *u, *d, *buff; + size_t size; + + /* run through two times. First to figure out how + much of a buffer we need. Second to do the + actual marshalling */ + + setnetgrent(argv[1]); + irp_marshall_ng_start(argv[1], NULL, &size); + while (getnetgrent(&h, &u, &d) == 1) + irp_marshall_ng_next(h, u, d, NULL, &size); + irp_marshall_ng_end(NULL, &size); + endnetgrent(argv[1]); + + buff = malloc(size); + + setnetgrent(argv[1]); + if (irp_marshall_ng_start(argv[1], buff, &size) != 0) + printf("Marshalling start failed\n"); + + while (getnetgrent(&h, &u, &d) == 1) { + if (irp_marshall_ng_next(h, u, d, buff, &size) + != 0) { + printf("Marshalling failed\n"); + } + } + + irp_marshall_ng_end(buff, &size); + endnetgrent(); + + printf("success: %s\n", buff); + } + break; + } + + + + case 'h': { + struct hostent he, *hp; + int i; + + + if (strchr(argv[1], '@') != NULL) { + if (irp_unmarshall_ho(&he, argv[1]) != 0) { + printf("unmarshall failed\n"); + exit(1); + } + + printf("Host: \"%s\"\nAliases:", he.h_name); + for (i = 0 ; he.h_aliases[i] != NULL ; i++) + printf("\n\t\t\"%s\"", he.h_aliases[i]); + printf("\nAddr Type: \"%s\"\n", + ADDR_T_STR(he.h_addrtype)); + printf("Length: %d\nAddresses:", he.h_length); + for (i = 0 ; he.h_addr_list[i] != 0 ; i++) { + inet_ntop(he.h_addrtype, he.h_addr_list[i], + buffer, sizeof buffer); + printf("\n\t\"%s\"\n", buffer); + } + printf("\n\n"); + + irp_marshall_ho(&he, &b, &len); + if (strcmp(argv[1], buffer) != 0) { + printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n", + buffer, argv[1]); + } else { + printf("compare ok\n"); + } + } else { + if ((hp = gethostbyname(argv[1])) == NULL) { + perror("gethostbyname"); + printf("\"%s\"\n", argv[1]); + exit(1); + } + + if (irp_marshall_ho(hp, &b, &len) != 0) { + printf("irp_marshall_ho failed\n"); + exit(1); + } + + printf("success: \"%s\"\n", buffer); + } + break; + } + + + case 's': { + struct servent *sv; + struct servent sv1; + + if (strchr(argv[1], ':') != NULL) { + sv = &sv1; + memset(sv, 0xef, sizeof (struct servent)); + if (irp_unmarshall_sv(sv, argv[1]) != 0) { + printf("unmarshall failed\n"); + + } + + irp_marshall_sv(sv, &b, &len); + if (strcmp(argv[1], buffer) != 0) { + printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n", + buffer, argv[1]); + } else { + printf("compare ok\n"); + } + } else { + if ((sv = getservbyname(argv[1], argv[2])) == NULL) { + perror("getservent"); + exit(1); + } + + if (irp_marshall_sv(sv, &b, &len) != 0) { + printf("irp_marshall_sv failed\n"); + exit(1); + } + + printf("success: \"%s\"\n", buffer); + } + break; + } + + case 'g': { + struct group *gr; + struct group gr1; + + if (strchr(argv[1], ':') != NULL) { + gr = &gr1; + memset(gr, 0xef, sizeof (struct group)); + if (irp_unmarshall_gr(gr, argv[1]) != 0) { + printf("unmarshall failed\n"); + + } + + irp_marshall_gr(gr, &b, &len); + if (strcmp(argv[1], buffer) != 0) { + printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n", + buffer, argv[1]); + } else { + printf("compare ok\n"); + } + } else { + if ((gr = getgrnam(argv[1])) == NULL) { + perror("getgrnam"); + exit(1); + } + + if (irp_marshall_gr(gr, &b, &len) != 0) { + printf("irp_marshall_gr failed\n"); + exit(1); + } + + printf("success: \"%s\"\n", buffer); + } + break; + } + + + case 'p': { + struct passwd *pw; + struct passwd pw1; + + if (strchr(argv[1], ':') != NULL) { + pw = &pw1; + memset(pw, 0xef, sizeof (*pw)); + if (irp_unmarshall_pw(pw, argv[1]) != 0) { + printf("unmarshall failed\n"); + exit(1); + } + + printf("User: \"%s\"\nPasswd: \"%s\"\nUid: %ld\nGid: %ld\n", + pw->pw_name, pw->pw_passwd, (long)pw->pw_uid, + (long)pw->pw_gid); + printf("Class: \"%s\"\nChange: %ld\nGecos: \"%s\"\n", + pw->pw_class, (long)pw->pw_change, pw->pw_gecos); + printf("Shell: \"%s\"\nDirectory: \"%s\"\n", + pw->pw_shell, pw->pw_dir); + + pw = getpwnam(pw->pw_name); + irp_marshall_pw(pw, &b, &len); + if (strcmp(argv[1], buffer) != 0) { + printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n", + buffer, argv[1]); + } else { + printf("compare ok\n"); + } + } else { + if ((pw = getpwnam(argv[1])) == NULL) { + perror("getpwnam"); + exit(1); + } + + if (irp_marshall_pw(pw, &b, &len) != 0) { + printf("irp_marshall_pw failed\n"); + exit(1); + } + + printf("success: \"%s\"\n", buffer); + } + break; + } + + default: + printf("Wrong option: %c\n", option); + break; + } + +#endif + + return (0); +} + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irs_data.c b/usr/src/lib/libresolv2_joy/common/irs/irs_data.c new file mode 100644 index 0000000000..3087b1189d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irs_data.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#ifndef __BIND_NOSTATIC + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> +#include <isc/memcluster.h> + +#ifdef DO_PTHREADS +#include <pthread.h> +#endif + +#include <irs.h> +#include <stdlib.h> + +#include "port_after.h" + +#include "irs_data.h" +#undef _res +#if !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) +#undef h_errno +extern int h_errno; +#endif + +extern struct __res_state _res; + +#ifdef DO_PTHREADS +static pthread_key_t key; +static int once = 0; +#else +static struct net_data *net_data; +#endif + +void +irs_destroy(void) { +#ifndef DO_PTHREADS + if (net_data != NULL) + net_data_destroy(net_data); + net_data = NULL; +#endif +} + +void +net_data_destroy(void *p) { + struct net_data *net_data = p; + + res_ndestroy(net_data->res); + if (net_data->gr != NULL) { + (*net_data->gr->close)(net_data->gr); + net_data->gr = NULL; + } + if (net_data->pw != NULL) { + (*net_data->pw->close)(net_data->pw); + net_data->pw = NULL; + } + if (net_data->sv != NULL) { + (*net_data->sv->close)(net_data->sv); + net_data->sv = NULL; + } + if (net_data->pr != NULL) { + (*net_data->pr->close)(net_data->pr); + net_data->pr = NULL; + } + if (net_data->ho != NULL) { + (*net_data->ho->close)(net_data->ho); + net_data->ho = NULL; + } + if (net_data->nw != NULL) { + (*net_data->nw->close)(net_data->nw); + net_data->nw = NULL; + } + if (net_data->ng != NULL) { + (*net_data->ng->close)(net_data->ng); + net_data->ng = NULL; + } + if (net_data->ho_data != NULL) { + free(net_data->ho_data); + net_data->ho_data = NULL; + } + if (net_data->nw_data != NULL) { + free(net_data->nw_data); + net_data->nw_data = NULL; + } + + (*net_data->irs->close)(net_data->irs); + memput(net_data, sizeof *net_data); +} + +/*% + * applications that need a specific config file other than + * _PATH_IRS_CONF should call net_data_init directly rather than letting + * the various wrapper functions make the first call. - brister + */ + +struct net_data * +net_data_init(const char *conf_file) { +#ifdef DO_PTHREADS +#ifndef LIBBIND_MUTEX_INITIALIZER +#define LIBBIND_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif + static pthread_mutex_t keylock = LIBBIND_MUTEX_INITIALIZER; + struct net_data *net_data; + + if (!once) { + if (pthread_mutex_lock(&keylock) != 0) + return (NULL); + if (!once) { + if (pthread_key_create(&key, net_data_destroy) != 0) { + (void)pthread_mutex_unlock(&keylock); + return (NULL); + } + once = 1; + } + if (pthread_mutex_unlock(&keylock) != 0) + return (NULL); + } + net_data = pthread_getspecific(key); +#endif + + if (net_data == NULL) { + net_data = net_data_create(conf_file); + if (net_data == NULL) + return (NULL); +#ifdef DO_PTHREADS + if (pthread_setspecific(key, net_data) != 0) { + net_data_destroy(net_data); + return (NULL); + } +#endif + } + + return (net_data); +} + +struct net_data * +net_data_create(const char *conf_file) { + struct net_data *net_data; + + net_data = memget(sizeof (struct net_data)); + if (net_data == NULL) + return (NULL); + memset(net_data, 0, sizeof (struct net_data)); + + if ((net_data->irs = irs_gen_acc("", conf_file)) == NULL) { + memput(net_data, sizeof (struct net_data)); + return (NULL); + } +#ifndef DO_PTHREADS + (*net_data->irs->res_set)(net_data->irs, &_res, NULL); +#endif + + net_data->res = (*net_data->irs->res_get)(net_data->irs); + if (net_data->res == NULL) { + (*net_data->irs->close)(net_data->irs); + memput(net_data, sizeof (struct net_data)); + return (NULL); + } + + if ((net_data->res->options & RES_INIT) == 0U && + res_ninit(net_data->res) == -1) { + (*net_data->irs->close)(net_data->irs); + memput(net_data, sizeof (struct net_data)); + return (NULL); + } + + return (net_data); +} + +void +net_data_minimize(struct net_data *net_data) { + res_nclose(net_data->res); +} + +#ifdef _REENTRANT +struct __res_state * +__res_state(void) { + /* NULL param here means use the default config file. */ + struct net_data *net_data = net_data_init(NULL); + if (net_data && net_data->res) + return (net_data->res); + + return (&_res); +} +#else +#ifdef __linux +struct __res_state * +__res_state(void) { + return (&_res); +} +#endif +#endif + +int * +__h_errno(void) { + /* NULL param here means use the default config file. */ + struct net_data *net_data = net_data_init(NULL); + if (net_data && net_data->res) + return (&net_data->res->res_h_errno); +#ifdef ORIGINAL_ISC_CODE +#if !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) + return(&_res.res_h_errno); +#else + return (&h_errno); +#endif +#else + return (&h_errno); +#endif /* ORIGINAL_ISC_CODE */ +} + +void +__h_errno_set(struct __res_state *res, int err) { + + +#if (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) + res->res_h_errno = err; +#else + h_errno = res->res_h_errno = err; +#endif +} + +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irs_data.h b/usr/src/lib/libresolv2_joy/common/irs/irs_data.h new file mode 100644 index 0000000000..cb814fd8b1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irs_data.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: irs_data.h,v 1.3 2005/04/27 04:56:30 sra Exp $ + */ + +#ifndef __BIND_NOSTATIC + +#define net_data_init __net_data_init + +struct net_data { + struct irs_acc * irs; + + struct irs_gr * gr; + struct irs_pw * pw; + struct irs_sv * sv; + struct irs_pr * pr; + struct irs_ho * ho; + struct irs_nw * nw; + struct irs_ng * ng; + + struct group * gr_last; + struct passwd * pw_last; + struct servent * sv_last; + struct protoent * pr_last; + struct netent * nw_last; /*%< should have been ne_last */ + struct nwent * nww_last; + struct hostent * ho_last; + + unsigned int gr_stayopen :1; + unsigned int pw_stayopen :1; + unsigned int sv_stayopen :1; + unsigned int pr_stayopen :1; + unsigned int ho_stayopen :1; + unsigned int nw_stayopen :1; + + void * nw_data; + void * ho_data; + + struct __res_state * res; /*%< for gethostent.c */ +}; + +extern struct net_data * net_data_init(const char *conf_file); +extern void net_data_minimize(struct net_data *); + +#endif /*__BIND_NOSTATIC*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/irs_p.h b/usr/src/lib/libresolv2_joy/common/irs/irs_p.h new file mode 100644 index 0000000000..2a0a933fce --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irs_p.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: irs_p.h,v 1.3 2005/04/27 04:56:30 sra Exp $ + */ + +#ifndef _IRS_P_H_INCLUDED +#define _IRS_P_H_INCLUDED + +#include <stdio.h> + +#include "pathnames.h" + +#define IRS_SV_MAXALIASES 35 + +struct lcl_sv { + FILE * fp; + char line[BUFSIZ+1]; + struct servent serv; + char * serv_aliases[IRS_SV_MAXALIASES]; +}; + +#define irs_nul_ng __irs_nul_ng +#define map_v4v6_address __map_v4v6_address +#define make_group_list __make_group_list +#define irs_lclsv_fnxt __irs_lclsv_fnxt + +extern void map_v4v6_address(const char *src, char *dst); +extern int make_group_list(struct irs_gr *, const char *, + gid_t, gid_t *, int *); +extern struct irs_ng * irs_nul_ng(struct irs_acc *); +extern struct servent * irs_lclsv_fnxt(struct lcl_sv *); + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl.c b/usr/src/lib/libresolv2_joy/common/irs/lcl.c new file mode 100644 index 0000000000..3a34b75c6d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <isc/memcluster.h> + +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "lcl_p.h" + +/* Forward. */ + +static void lcl_close(struct irs_acc *); +static struct __res_state * lcl_res_get(struct irs_acc *); +static void lcl_res_set(struct irs_acc *, struct __res_state *, + void (*)(void *)); + +/* Public */ + +struct irs_acc * +irs_lcl_acc(const char *options) { + struct irs_acc *acc; + struct lcl_p *lcl; + + UNUSED(options); + + if (!(acc = memget(sizeof *acc))) { + errno = ENOMEM; + return (NULL); + } + memset(acc, 0x5e, sizeof *acc); + if (!(lcl = memget(sizeof *lcl))) { + errno = ENOMEM; + free(acc); + return (NULL); + } + memset(lcl, 0x5e, sizeof *lcl); + lcl->res = NULL; + lcl->free_res = NULL; + acc->private = lcl; +#ifdef WANT_IRS_GR + acc->gr_map = irs_lcl_gr; +#else + acc->gr_map = NULL; +#endif +#ifdef WANT_IRS_PW + acc->pw_map = irs_lcl_pw; +#else + acc->pw_map = NULL; +#endif + acc->sv_map = irs_lcl_sv; + acc->pr_map = irs_lcl_pr; + acc->ho_map = irs_lcl_ho; + acc->nw_map = irs_lcl_nw; + acc->ng_map = irs_lcl_ng; + acc->res_get = lcl_res_get; + acc->res_set = lcl_res_set; + acc->close = lcl_close; + return (acc); +} + +/* Methods */ +static struct __res_state * +lcl_res_get(struct irs_acc *this) { + struct lcl_p *lcl = (struct lcl_p *)this->private; + + if (lcl->res == NULL) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (res == NULL) + return (NULL); + memset(res, 0, sizeof *res); + lcl_res_set(this, res, free); + } + + if ((lcl->res->options & RES_INIT) == 0U && + res_ninit(lcl->res) < 0) + return (NULL); + + return (lcl->res); +} + +static void +lcl_res_set(struct irs_acc *this, struct __res_state *res, + void (*free_res)(void *)) { + struct lcl_p *lcl = (struct lcl_p *)this->private; + + if (lcl->res && lcl->free_res) { + res_nclose(lcl->res); + (*lcl->free_res)(lcl->res); + } + + lcl->res = res; + lcl->free_res = free_res; +} + +static void +lcl_close(struct irs_acc *this) { + struct lcl_p *lcl = (struct lcl_p *)this->private; + + if (lcl) { + if (lcl->free_res) + (*lcl->free_res)(lcl->res); + memput(lcl, sizeof *lcl); + } + memput(this, sizeof *this); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c new file mode 100644 index 0000000000..e8893bf095 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c @@ -0,0 +1,574 @@ +/* + * 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. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* from gethostnamadr.c 8.1 (Berkeley) 6/4/93 */ +/* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */ + +/* Imports. */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "dns_p.h" +#include "lcl_p.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) sprintf x +#endif + +/* Definitions. */ + +#define MAXALIASES 35 +#define MAXADDRS 35 +#define Max(a,b) ((a) > (b) ? (a) : (b)) + +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif + +struct pvt { + FILE * fp; + struct hostent host; + char * h_addr_ptrs[MAXADDRS + 1]; + char * host_aliases[MAXALIASES]; + char hostbuf[8*1024]; + u_char host_addr[16]; /*%< IPv4 or IPv6 */ + struct __res_state *res; + void (*free_res)(void *); +}; + +typedef union { + int32_t al; + char ac; +} align; + +static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; +static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; + +/* Forward. */ + +static void ho_close(struct irs_ho *this); +static struct hostent * ho_byname(struct irs_ho *this, const char *name); +static struct hostent * ho_byname2(struct irs_ho *this, const char *name, + int af); +static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr, + int len, int af); +static struct hostent * ho_next(struct irs_ho *this); +static void ho_rewind(struct irs_ho *this); +static void ho_minimize(struct irs_ho *this); +static struct __res_state * ho_res_get(struct irs_ho *this); +static void ho_res_set(struct irs_ho *this, + struct __res_state *res, + void (*free_res)(void *)); +static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, + const struct addrinfo *pai); + +static size_t ns_namelen(const char *); +static int init(struct irs_ho *this); + +/* Portability. */ + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + +/* Public. */ + +struct irs_ho * +irs_lcl_ho(struct irs_acc *this) { + struct irs_ho *ho; + struct pvt *pvt; + + UNUSED(this); + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + if (!(ho = memget(sizeof *ho))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(ho, 0x5e, sizeof *ho); + ho->private = pvt; + ho->close = ho_close; + ho->byname = ho_byname; + ho->byname2 = ho_byname2; + ho->byaddr = ho_byaddr; + ho->next = ho_next; + ho->rewind = ho_rewind; + ho->minimize = ho_minimize; + ho->res_get = ho_res_get; + ho->res_set = ho_res_set; + ho->addrinfo = ho_addrinfo; + return (ho); +} + +/* Methods. */ + +static void +ho_close(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + ho_minimize(this); + if (pvt->fp) + (void) fclose(pvt->fp); + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct hostent * +ho_byname(struct irs_ho *this, const char *name) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *hp; + + if (init(this) == -1) + return (NULL); + + if (pvt->res->options & RES_USE_INET6) { + hp = ho_byname2(this, name, AF_INET6); + if (hp) + return (hp); + } + return (ho_byname2(this, name, AF_INET)); +} + +static struct hostent * +ho_byname2(struct irs_ho *this, const char *name, int af) { + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *hp; + char **hap; + size_t n; + + if (init(this) == -1) + return (NULL); + + ho_rewind(this); + n = ns_namelen(name); + while ((hp = ho_next(this)) != NULL) { + size_t nn; + + if (hp->h_addrtype != af) + continue; + nn = ns_namelen(hp->h_name); + if (strncasecmp(hp->h_name, name, Max(n, nn)) == 0) + goto found; + for (hap = hp->h_aliases; *hap; hap++) { + nn = ns_namelen(*hap); + if (strncasecmp(*hap, name, Max(n, nn)) == 0) + goto found; + } + } + found: + if (!hp) { + RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); + return (NULL); + } + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + return (hp); +} + +static struct hostent * +ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { + struct pvt *pvt = (struct pvt *)this->private; + const u_char *uaddr = addr; + struct hostent *hp; + int size; + + if (init(this) == -1) + return (NULL); + + if (af == AF_INET6 && len == IN6ADDRSZ && + (!memcmp(uaddr, mapped, sizeof mapped) || + !memcmp(uaddr, tunnelled, sizeof tunnelled))) { + /* Unmap. */ + addr = (const u_char *)addr + sizeof mapped; + uaddr += sizeof mapped; + af = AF_INET; + len = INADDRSZ; + } + switch (af) { + case AF_INET: + size = INADDRSZ; + break; + case AF_INET6: + size = IN6ADDRSZ; + break; + default: + errno = EAFNOSUPPORT; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + if (size > len) { + errno = EINVAL; + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + + /* + * Do the search. + */ + ho_rewind(this); + while ((hp = ho_next(this)) != NULL) { + char **hap; + + for (hap = hp->h_addr_list; *hap; hap++) { + const u_char *taddr = (const u_char *)*hap; + int taf = hp->h_addrtype; + int tlen = hp->h_length; + + if (taf == AF_INET6 && tlen == IN6ADDRSZ && + (!memcmp(taddr, mapped, sizeof mapped) || + !memcmp(taddr, tunnelled, sizeof tunnelled))) { + /* Unmap. */ + taddr += sizeof mapped; + taf = AF_INET; + tlen = INADDRSZ; + } + if (taf == af && tlen == len && + !memcmp(taddr, uaddr, tlen)) + goto found; + } + } + found: + if (!hp) { + RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); + return (NULL); + } + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + return (hp); +} + +static struct hostent * +ho_next(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + char *cp, **q, *p; + char *bufp, *ndbuf, *dbuf = NULL; + int c, af, len, bufsiz, offset; + + if (init(this) == -1) + return (NULL); + + if (!pvt->fp) + ho_rewind(this); + if (!pvt->fp) { + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + bufp = pvt->hostbuf; + bufsiz = sizeof pvt->hostbuf; + offset = 0; + again: + if (!(p = fgets(bufp + offset, bufsiz - offset, pvt->fp))) { + RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); + if (dbuf) + free(dbuf); + return (NULL); + } + if (!strchr(p, '\n') && !feof(pvt->fp)) { +#define GROWBUF 1024 + /* allocate space for longer line */ + if (dbuf == NULL) { + if ((ndbuf = malloc(bufsiz + GROWBUF)) != NULL) + strcpy(ndbuf, bufp); + } else + ndbuf = realloc(dbuf, bufsiz + GROWBUF); + if (ndbuf) { + dbuf = ndbuf; + bufp = dbuf; + bufsiz += GROWBUF; + offset = strlen(dbuf); + } else { + /* allocation failed; skip this long line */ + while ((c = getc(pvt->fp)) != EOF) + if (c == '\n') + break; + if (c != EOF) + ungetc(c, pvt->fp); + } + goto again; + } + + p -= offset; + offset = 0; + + if (*p == '#') + goto again; + if ((cp = strpbrk(p, "#\n")) != NULL) + *cp = '\0'; + if (!(cp = strpbrk(p, " \t"))) + goto again; + *cp++ = '\0'; + if (inet_pton(AF_INET6, p, pvt->host_addr) > 0) { + af = AF_INET6; + len = IN6ADDRSZ; + } else if (inet_aton(p, (struct in_addr *)pvt->host_addr) > 0) { + if (pvt->res->options & RES_USE_INET6) { + map_v4v6_address((char*)pvt->host_addr, + (char*)pvt->host_addr); + af = AF_INET6; + len = IN6ADDRSZ; + } else { + af = AF_INET; + len = INADDRSZ; + } + } else { + goto again; + } + pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; + pvt->h_addr_ptrs[1] = NULL; + pvt->host.h_addr_list = pvt->h_addr_ptrs; + pvt->host.h_length = len; + pvt->host.h_addrtype = af; + while (*cp == ' ' || *cp == '\t') + cp++; + pvt->host.h_name = cp; + q = pvt->host.h_aliases = pvt->host_aliases; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &pvt->host_aliases[MAXALIASES - 1]) + *q++ = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + } + *q = NULL; + if (dbuf) + free(dbuf); + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + return (&pvt->host); +} + +static void +ho_rewind(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp) { + if (fseek(pvt->fp, 0L, SEEK_SET) == 0) + return; + (void)fclose(pvt->fp); + } + if (!(pvt->fp = fopen(_PATH_HOSTS, "r"))) + return; + if (fcntl(fileno(pvt->fp), F_SETFD, 1) < 0) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } +} + +static void +ho_minimize(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp != NULL) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } + if (pvt->res) + res_nclose(pvt->res); +} + +static struct __res_state * +ho_res_get(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + ho_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +ho_res_set(struct irs_ho *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; +} + +struct lcl_res_target { + struct lcl_res_target *next; + int family; +}; + +/* XXX */ +extern struct addrinfo *hostent2addrinfo __P((struct hostent *, + const struct addrinfo *pai)); + +static struct addrinfo * +ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) +{ + struct pvt *pvt = (struct pvt *)this->private; + struct hostent *hp; + struct lcl_res_target q, q2, *p; + struct addrinfo sentinel, *cur; + + memset(&q, 0, sizeof(q2)); + memset(&q2, 0, sizeof(q2)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + switch(pai->ai_family) { + case AF_UNSPEC: /*%< INET6 then INET4 */ + q.family = AF_INET6; + q.next = &q2; + q2.family = AF_INET; + break; + case AF_INET6: + q.family = AF_INET6; + break; + case AF_INET: + q.family = AF_INET; + break; + default: + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /*%< ??? */ + return(NULL); + } + + for (p = &q; p; p = p->next) { + struct addrinfo *ai; + + hp = (*this->byname2)(this, name, p->family); + if (hp == NULL) { + /* byname2 should've set an appropriate error */ + continue; + } + if ((hp->h_name == NULL) || (hp->h_name[0] == 0) || + (hp->h_addr_list[0] == NULL)) { + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + continue; + } + + ai = hostent2addrinfo(hp, pai); + if (ai) { + cur->ai_next = ai; + while (cur->ai_next) + cur = cur->ai_next; + } + } + + if (sentinel.ai_next == NULL) + RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND); + + return(sentinel.ai_next); +} + +/* Private. */ + +static size_t +ns_namelen(const char *s) { + int i; + + for (i = strlen(s); i > 0 && s[i-1] == '.'; i--) + (void)NULL; + return ((size_t) i); +} + +static int +init(struct irs_ho *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res && !ho_res_get(this)) + return (-1); + if (((pvt->res->options & RES_INIT) == 0U) && + res_ninit(pvt->res) == -1) + return (-1); + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c new file mode 100644 index 0000000000..089a3c437e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "lcl_p.h" + +/* Definitions */ + +#define NG_HOST 0 /*%< Host name */ +#define NG_USER 1 /*%< User name */ +#define NG_DOM 2 /*%< and Domain name */ +#define LINSIZ 1024 /*%< Length of netgroup file line */ +/* + * XXX Warning XXX + * This code is a hack-and-slash special. It realy needs to be + * rewritten with things like strdup, and realloc in mind. + * More reasonable data structures would not be a bad thing. + */ + +/*% + * Static Variables and functions used by setnetgrent(), getnetgrent() and + * endnetgrent(). + * + * There are two linked lists: + * \li linelist is just used by setnetgrent() to parse the net group file via. + * parse_netgrp() + * \li netgrp is the list of entries for the current netgroup + */ +struct linelist { + struct linelist *l_next; /*%< Chain ptr. */ + int l_parsed; /*%< Flag for cycles */ + char * l_groupname; /*%< Name of netgroup */ + char * l_line; /*%< Netgroup entrie(s) to be parsed */ +}; + +struct ng_old_struct { + struct ng_old_struct *ng_next; /*%< Chain ptr */ + char * ng_str[3]; /*%< Field pointers, see below */ +}; + +struct pvt { + FILE *fp; + struct linelist *linehead; + struct ng_old_struct *nextgrp; + struct { + struct ng_old_struct *gr; + char *grname; + } grouphead; +}; + +/* Forward */ + +static void ng_rewind(struct irs_ng *, const char*); +static void ng_close(struct irs_ng *); +static int ng_next(struct irs_ng *, const char **, + const char **, const char **); +static int ng_test(struct irs_ng *, const char *, + const char *, const char *, + const char *); +static void ng_minimize(struct irs_ng *); + +static int parse_netgrp(struct irs_ng *, const char*); +static struct linelist *read_for_group(struct irs_ng *, const char *); +static void freelists(struct irs_ng *); + +/* Public */ + +struct irs_ng * +irs_lcl_ng(struct irs_acc *this) { + struct irs_ng *ng; + struct pvt *pvt; + + UNUSED(this); + + if (!(ng = memget(sizeof *ng))) { + errno = ENOMEM; + return (NULL); + } + memset(ng, 0x5e, sizeof *ng); + if (!(pvt = memget(sizeof *pvt))) { + memput(ng, sizeof *ng); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + ng->private = pvt; + ng->close = ng_close; + ng->next = ng_next; + ng->test = ng_test; + ng->rewind = ng_rewind; + ng->minimize = ng_minimize; + return (ng); +} + +/* Methods */ + +static void +ng_close(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp != NULL) + fclose(pvt->fp); + freelists(this); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +/*% + * Parse the netgroup file looking for the netgroup and build the list + * of netgrp structures. Let parse_netgrp() and read_for_group() do + * most of the work. + */ +static void +ng_rewind(struct irs_ng *this, const char *group) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp != NULL && fseek(pvt->fp, SEEK_CUR, 0L) == -1) { + fclose(pvt->fp); + pvt->fp = NULL; + } + + if (pvt->fp == NULL || pvt->grouphead.gr == NULL || + strcmp(group, pvt->grouphead.grname)) { + freelists(this); + if (pvt->fp != NULL) + fclose(pvt->fp); + pvt->fp = fopen(_PATH_NETGROUP, "r"); + if (pvt->fp != NULL) { + if (parse_netgrp(this, group)) + freelists(this); + if (!(pvt->grouphead.grname = strdup(group))) + freelists(this); + fclose(pvt->fp); + pvt->fp = NULL; + } + } + pvt->nextgrp = pvt->grouphead.gr; +} + +/*% + * Get the next netgroup off the list. + */ +static int +ng_next(struct irs_ng *this, const char **host, const char **user, + const char **domain) +{ + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->nextgrp) { + *host = pvt->nextgrp->ng_str[NG_HOST]; + *user = pvt->nextgrp->ng_str[NG_USER]; + *domain = pvt->nextgrp->ng_str[NG_DOM]; + pvt->nextgrp = pvt->nextgrp->ng_next; + return (1); + } + return (0); +} + +/*% + * Search for a match in a netgroup. + */ +static int +ng_test(struct irs_ng *this, const char *name, + const char *host, const char *user, const char *domain) +{ + const char *ng_host, *ng_user, *ng_domain; + + ng_rewind(this, name); + while (ng_next(this, &ng_host, &ng_user, &ng_domain)) + if ((host == NULL || ng_host == NULL || + !strcmp(host, ng_host)) && + (user == NULL || ng_user == NULL || + !strcmp(user, ng_user)) && + (domain == NULL || ng_domain == NULL || + !strcmp(domain, ng_domain))) { + freelists(this); + return (1); + } + freelists(this); + return (0); +} + +static void +ng_minimize(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp != NULL) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } +} + +/* Private */ + +/*% + * endnetgrent() - cleanup + */ +static void +freelists(struct irs_ng *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct linelist *lp, *olp; + struct ng_old_struct *gp, *ogp; + + lp = pvt->linehead; + while (lp) { + olp = lp; + lp = lp->l_next; + free(olp->l_groupname); + free(olp->l_line); + free((char *)olp); + } + pvt->linehead = NULL; + if (pvt->grouphead.grname) { + free(pvt->grouphead.grname); + pvt->grouphead.grname = NULL; + } + gp = pvt->grouphead.gr; + while (gp) { + ogp = gp; + gp = gp->ng_next; + if (ogp->ng_str[NG_HOST]) + free(ogp->ng_str[NG_HOST]); + if (ogp->ng_str[NG_USER]) + free(ogp->ng_str[NG_USER]); + if (ogp->ng_str[NG_DOM]) + free(ogp->ng_str[NG_DOM]); + free((char *)ogp); + } + pvt->grouphead.gr = NULL; +} + +/*% + * Parse the netgroup file setting up the linked lists. + */ +static int +parse_netgrp(struct irs_ng *this, const char *group) { + struct pvt *pvt = (struct pvt *)this->private; + char *spos, *epos; + int len, strpos; + char *pos, *gpos; + struct ng_old_struct *grp; + struct linelist *lp = pvt->linehead; + + /* + * First, see if the line has already been read in. + */ + while (lp) { + if (!strcmp(group, lp->l_groupname)) + break; + lp = lp->l_next; + } + if (lp == NULL && + (lp = read_for_group(this, group)) == NULL) + return (1); + if (lp->l_parsed) { + /*fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);*/ + return (1); + } else + lp->l_parsed = 1; + pos = lp->l_line; + while (*pos != '\0') { + if (*pos == '(') { + if (!(grp = malloc(sizeof (struct ng_old_struct)))) { + freelists(this); + errno = ENOMEM; + return (1); + } + memset(grp, 0, sizeof (struct ng_old_struct)); + grp->ng_next = pvt->grouphead.gr; + pvt->grouphead.gr = grp; + pos++; + gpos = strsep(&pos, ")"); + for (strpos = 0; strpos < 3; strpos++) { + if ((spos = strsep(&gpos, ","))) { + while (*spos == ' ' || *spos == '\t') + spos++; + if ((epos = strpbrk(spos, " \t"))) { + *epos = '\0'; + len = epos - spos; + } else + len = strlen(spos); + if (len > 0) { + if(!(grp->ng_str[strpos] + = (char *) + malloc(len + 1))) { + freelists(this); + return (1); + } + memcpy(grp->ng_str[strpos], + spos, + len + 1); + } + } else + goto errout; + } + } else { + spos = strsep(&pos, ", \t"); + if (spos != NULL && parse_netgrp(this, spos)) { + freelists(this); + return (1); + } + } + if (pos == NULL) + break; + while (*pos == ' ' || *pos == ',' || *pos == '\t') + pos++; + } + return (0); + errout: + /*fprintf(stderr, "Bad netgroup %s at ..%s\n", lp->l_groupname, + spos);*/ + return (1); +} + +/*% + * Read the netgroup file and save lines until the line for the netgroup + * is found. Return 1 if eof is encountered. + */ +static struct linelist * +read_for_group(struct irs_ng *this, const char *group) { + struct pvt *pvt = (struct pvt *)this->private; + char *pos, *spos, *linep = NULL, *olinep; + int len, olen, cont; + struct linelist *lp; + char line[LINSIZ + 1]; + + while (fgets(line, LINSIZ, pvt->fp) != NULL) { + pos = line; + if (*pos == '#') + continue; + while (*pos == ' ' || *pos == '\t') + pos++; + spos = pos; + while (*pos != ' ' && *pos != '\t' && *pos != '\n' && + *pos != '\0') + pos++; + len = pos - spos; + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos != '\n' && *pos != '\0') { + if (!(lp = malloc(sizeof (*lp)))) { + freelists(this); + return (NULL); + } + lp->l_parsed = 0; + if (!(lp->l_groupname = malloc(len + 1))) { + free(lp); + freelists(this); + return (NULL); + } + memcpy(lp->l_groupname, spos, len); + *(lp->l_groupname + len) = '\0'; + len = strlen(pos); + olen = 0; + olinep = NULL; + + /* + * Loop around handling line continuations. + */ + do { + if (*(pos + len - 1) == '\n') + len--; + if (*(pos + len - 1) == '\\') { + len--; + cont = 1; + } else + cont = 0; + if (len > 0) { + if (!(linep = malloc(olen + len + 1))){ + if (olen > 0) + free(olinep); + free(lp->l_groupname); + free(lp); + freelists(this); + errno = ENOMEM; + return (NULL); + } + if (olen > 0) { + memcpy(linep, olinep, olen); + free(olinep); + } + memcpy(linep + olen, pos, len); + olen += len; + *(linep + olen) = '\0'; + olinep = linep; + } + if (cont) { + if (fgets(line, LINSIZ, pvt->fp)) { + pos = line; + len = strlen(pos); + } else + cont = 0; + } + } while (cont); + lp->l_line = linep; + lp->l_next = pvt->linehead; + pvt->linehead = lp; + + /* + * If this is the one we wanted, we are done. + */ + if (!strcmp(lp->l_groupname, group)) + return (lp); + } + } + return (NULL); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c new file mode 100644 index 0000000000..e1c5837944 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 1989, 1993, 1995 + * 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. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* from getgrent.c 8.2 (Berkeley) 3/21/94"; */ +/* from BSDI Id: getgrent.c,v 2.8 1996/05/28 18:15:14 bostic Exp $ */ + +/* Imports */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <fcntl.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include <isc/misc.h> +#include "irs_p.h" +#include "lcl_p.h" + +#define MAXALIASES 35 +#define MAXADDRSIZE 4 + +struct pvt { + FILE * fp; + char line[BUFSIZ+1]; + struct nwent net; + char * aliases[MAXALIASES]; + char addr[MAXADDRSIZE]; + struct __res_state * res; + void (*free_res)(void *); +}; + +/* Forward */ + +static void nw_close(struct irs_nw *); +static struct nwent * nw_byname(struct irs_nw *, const char *, int); +static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int); +static struct nwent * nw_next(struct irs_nw *); +static void nw_rewind(struct irs_nw *); +static void nw_minimize(struct irs_nw *); +static struct __res_state * nw_res_get(struct irs_nw *this); +static void nw_res_set(struct irs_nw *this, + struct __res_state *res, + void (*free_res)(void *)); + +static int init(struct irs_nw *this); + +/* Portability. */ + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + +/* Public */ + +struct irs_nw * +irs_lcl_nw(struct irs_acc *this) { + struct irs_nw *nw; + struct pvt *pvt; + + UNUSED(this); + + if (!(pvt = memget(sizeof *pvt))) { + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + if (!(nw = memget(sizeof *nw))) { + memput(pvt, sizeof *pvt); + errno = ENOMEM; + return (NULL); + } + memset(nw, 0x5e, sizeof *nw); + nw->private = pvt; + nw->close = nw_close; + nw->byname = nw_byname; + nw->byaddr = nw_byaddr; + nw->next = nw_next; + nw->rewind = nw_rewind; + nw->minimize = nw_minimize; + nw->res_get = nw_res_get; + nw->res_set = nw_res_set; + return (nw); +} + +/* Methods */ + +static void +nw_close(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + nw_minimize(this); + if (pvt->res && pvt->free_res) + (*pvt->free_res)(pvt->res); + if (pvt->fp) + (void)fclose(pvt->fp); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct nwent * +nw_byaddr(struct irs_nw *this, void *net, int length, int type) { + struct nwent *p; + + if (init(this) == -1) + return(NULL); + + nw_rewind(this); + while ((p = nw_next(this)) != NULL) + if (p->n_addrtype == type && p->n_length == length) + if (bitncmp(p->n_addr, net, length) == 0) + break; + return (p); +} + +static struct nwent * +nw_byname(struct irs_nw *this, const char *name, int type) { + struct nwent *p; + char **ap; + + if (init(this) == -1) + return(NULL); + + nw_rewind(this); + while ((p = nw_next(this)) != NULL) { + if (ns_samename(p->n_name, name) == 1 && + p->n_addrtype == type) + break; + for (ap = p->n_aliases; *ap; ap++) + if ((ns_samename(*ap, name) == 1) && + (p->n_addrtype == type)) + goto found; + } + found: + return (p); +} + +static void +nw_rewind(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp) { + if (fseek(pvt->fp, 0L, SEEK_SET) == 0) + return; + (void)fclose(pvt->fp); + } + if (!(pvt->fp = fopen(_PATH_NETWORKS, "r"))) + return; + if (fcntl(fileno(pvt->fp), F_SETFD, 1) < 0) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } +} + +static struct nwent * +nw_next(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + struct nwent *ret = NULL; + char *p, *cp, **q; + char *bufp, *ndbuf, *dbuf = NULL; + int c, bufsiz, offset = 0; + + if (init(this) == -1) + return(NULL); + + if (pvt->fp == NULL) + nw_rewind(this); + if (pvt->fp == NULL) { + RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); + return (NULL); + } + bufp = pvt->line; + bufsiz = sizeof(pvt->line); + + again: + p = fgets(bufp + offset, bufsiz - offset, pvt->fp); + if (p == NULL) + goto cleanup; + if (!strchr(p, '\n') && !feof(pvt->fp)) { +#define GROWBUF 1024 + /* allocate space for longer line */ + if (dbuf == NULL) { + if ((ndbuf = malloc(bufsiz + GROWBUF)) != NULL) + strcpy(ndbuf, bufp); + } else + ndbuf = realloc(dbuf, bufsiz + GROWBUF); + if (ndbuf) { + dbuf = ndbuf; + bufp = dbuf; + bufsiz += GROWBUF; + offset = strlen(dbuf); + } else { + /* allocation failed; skip this long line */ + while ((c = getc(pvt->fp)) != EOF) + if (c == '\n') + break; + if (c != EOF) + ungetc(c, pvt->fp); + } + goto again; + } + + p -= offset; + offset = 0; + + if (*p == '#') + goto again; + + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + pvt->net.n_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'; + pvt->net.n_length = inet_net_pton(AF_INET, cp, pvt->addr, + sizeof pvt->addr); + if (pvt->net.n_length < 0) + goto again; + pvt->net.n_addrtype = AF_INET; + pvt->net.n_addr = pvt->addr; + q = pvt->net.n_aliases = pvt->aliases; + if (p != NULL) { + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &pvt->aliases[MAXALIASES - 1]) + *q++ = cp; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + ret = &pvt->net; + + cleanup: + if (dbuf) + free(dbuf); + + return (ret); +} + +static void +nw_minimize(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res) + res_nclose(pvt->res); + if (pvt->fp != NULL) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } +} + +static struct __res_state * +nw_res_get(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (!res) { + errno = ENOMEM; + return (NULL); + } + memset(res, 0, sizeof *res); + nw_res_set(this, res, free); + } + + return (pvt->res); +} + +static void +nw_res_set(struct irs_nw *this, struct __res_state *res, + void (*free_res)(void *)) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->res && pvt->free_res) { + res_nclose(pvt->res); + (*pvt->free_res)(pvt->res); + } + + pvt->res = res; + pvt->free_res = free_res; +} + +static int +init(struct irs_nw *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (!pvt->res && !nw_res_get(this)) + return (-1); + if (((pvt->res->options & RES_INIT) == 0U) && + res_ninit(pvt->res) == -1) + return (-1); + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_p.h b/usr/src/lib/libresolv2_joy/common/irs/lcl_p.h new file mode 100644 index 0000000000..e3f4f009cb --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_p.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: lcl_p.h,v 1.3 2005/04/27 04:56:31 sra Exp $ + */ + +/*! \file + * \brief + * lcl_p.h - private include file for the local accessor functions. + */ + +#ifndef _LCL_P_H_INCLUDED +#define _LCL_P_H_INCLUDED + +/*% + * Object state. + */ +struct lcl_p { + struct __res_state * res; + void (*free_res) __P((void *)); +}; + +/* + * Externs. + */ + +extern struct irs_acc * irs_lcl_acc __P((const char *)); +extern struct irs_gr * irs_lcl_gr __P((struct irs_acc *)); +extern struct irs_pw * irs_lcl_pw __P((struct irs_acc *)); +extern struct irs_sv * irs_lcl_sv __P((struct irs_acc *)); +extern struct irs_pr * irs_lcl_pr __P((struct irs_acc *)); +extern struct irs_ho * irs_lcl_ho __P((struct irs_acc *)); +extern struct irs_nw * irs_lcl_nw __P((struct irs_acc *)); +extern struct irs_ng * irs_lcl_ng __P((struct irs_acc *)); + +#endif /*_LCL_P_H_INCLUDED*/ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c new file mode 100644 index 0000000000..22720f3e99 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 1989, 1993, 1995 + * 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. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "lcl_p.h" + +#ifndef _PATH_PROTOCOLS +#define _PATH_PROTOCOLS "/etc/protocols" +#endif +#define MAXALIASES 35 + +/* Types */ + +struct pvt { + FILE * fp; + char line[BUFSIZ+1]; + char * dbuf; + struct protoent proto; + char * proto_aliases[MAXALIASES]; +}; + +/* Forward */ + +static void pr_close(struct irs_pr *); +static struct protoent * pr_next(struct irs_pr *); +static struct protoent * pr_byname(struct irs_pr *, const char *); +static struct protoent * pr_bynumber(struct irs_pr *, int); +static void pr_rewind(struct irs_pr *); +static void pr_minimize(struct irs_pr *); + +/* Portability. */ + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + +/* Public */ + +struct irs_pr * +irs_lcl_pr(struct irs_acc *this) { + struct irs_pr *pr; + struct pvt *pvt; + + if (!(pr = memget(sizeof *pr))) { + errno = ENOMEM; + return (NULL); + } + if (!(pvt = memget(sizeof *pvt))) { + memput(pr, sizeof *this); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + pr->private = pvt; + pr->close = pr_close; + pr->byname = pr_byname; + pr->bynumber = pr_bynumber; + pr->next = pr_next; + pr->rewind = pr_rewind; + pr->minimize = pr_minimize; + pr->res_get = NULL; + pr->res_set = NULL; + return (pr); +} + +/* Methods */ + +static void +pr_close(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp) + (void) fclose(pvt->fp); + if (pvt->dbuf) + free(pvt->dbuf); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct protoent * +pr_byname(struct irs_pr *this, const char *name) { + + struct protoent *p; + char **cp; + + pr_rewind(this); + while ((p = pr_next(this))) { + if (!strcmp(p->p_name, name)) + goto found; + for (cp = p->p_aliases; *cp; cp++) + if (!strcmp(*cp, name)) + goto found; + } + found: + return (p); +} + +static struct protoent * +pr_bynumber(struct irs_pr *this, int proto) { + struct protoent *p; + + pr_rewind(this); + while ((p = pr_next(this))) + if (p->p_proto == proto) + break; + return (p); +} + +static void +pr_rewind(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp) { + if (fseek(pvt->fp, 0L, SEEK_SET) == 0) + return; + (void)fclose(pvt->fp); + } + if (!(pvt->fp = fopen(_PATH_PROTOCOLS, "r" ))) + return; + if (fcntl(fileno(pvt->fp), F_SETFD, 1) < 0) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } +} + +static struct protoent * +pr_next(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + char *p, *cp, **q; + char *bufp, *ndbuf, *dbuf = NULL; + int c, bufsiz, offset; + + if (!pvt->fp) + pr_rewind(this); + if (!pvt->fp) + return (NULL); + if (pvt->dbuf) { + free(pvt->dbuf); + pvt->dbuf = NULL; + } + bufp = pvt->line; + bufsiz = BUFSIZ; + offset = 0; + again: + if ((p = fgets(bufp + offset, bufsiz - offset, pvt->fp)) == NULL) { + if (dbuf) + free(dbuf); + return (NULL); + } + if (!strchr(p, '\n') && !feof(pvt->fp)) { +#define GROWBUF 1024 + /* allocate space for longer line */ + if (dbuf == NULL) { + if ((ndbuf = malloc(bufsiz + GROWBUF)) != NULL) + strcpy(ndbuf, bufp); + } else + ndbuf = realloc(dbuf, bufsiz + GROWBUF); + if (ndbuf) { + dbuf = ndbuf; + bufp = dbuf; + bufsiz += GROWBUF; + offset = strlen(dbuf); + } else { + /* allocation failed; skip this long line */ + while ((c = getc(pvt->fp)) != EOF) + if (c == '\n') + break; + if (c != EOF) + ungetc(c, pvt->fp); + } + goto again; + } + + p -= offset; + offset = 0; + + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp != NULL) + *cp = '\0'; + pvt->proto.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'; + pvt->proto.p_proto = atoi(cp); + q = pvt->proto.p_aliases = pvt->proto_aliases; + if (p != NULL) { + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &pvt->proto_aliases[MAXALIASES - 1]) + *q++ = cp; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + pvt->dbuf = dbuf; + return (&pvt->proto); +} + +static void +pr_minimize(struct irs_pr *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->fp != NULL) { + (void)fclose(pvt->fp); + pvt->fp = NULL; + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c new file mode 100644 index 0000000000..a1ef71be2a --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 1989, 1993, 1995 + * 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. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#ifdef IRS_LCL_SV_DB +#include <db.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "lcl_p.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Types */ + +struct pvt { +#ifdef IRS_LCL_SV_DB + DB * dbh; + int dbf; +#endif + struct lcl_sv sv; +}; + +/* Forward */ + +static void sv_close(struct irs_sv*); +static struct servent * sv_next(struct irs_sv *); +static struct servent * sv_byname(struct irs_sv *, const char *, + const char *); +static struct servent * sv_byport(struct irs_sv *, int, const char *); +static void sv_rewind(struct irs_sv *); +static void sv_minimize(struct irs_sv *); +/*global*/ struct servent * irs_lclsv_fnxt(struct lcl_sv *); +#ifdef IRS_LCL_SV_DB +static struct servent * sv_db_rec(struct lcl_sv *, DBT *, DBT *); +#endif + +/* Portability */ + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif + +/* Public */ + +struct irs_sv * +irs_lcl_sv(struct irs_acc *this) { + struct irs_sv *sv; + struct pvt *pvt; + + UNUSED(this); + + if ((sv = memget(sizeof *sv)) == NULL) { + errno = ENOMEM; + return (NULL); + } + memset(sv, 0x5e, sizeof *sv); + if ((pvt = memget(sizeof *pvt)) == NULL) { + memput(sv, sizeof *sv); + errno = ENOMEM; + return (NULL); + } + memset(pvt, 0, sizeof *pvt); + sv->private = pvt; + sv->close = sv_close; + sv->next = sv_next; + sv->byname = sv_byname; + sv->byport = sv_byport; + sv->rewind = sv_rewind; + sv->minimize = sv_minimize; + sv->res_get = NULL; + sv->res_set = NULL; +#ifdef IRS_LCL_SV_DB + pvt->dbf = R_FIRST; +#endif + return (sv); +} + +/* Methods */ + +static void +sv_close(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + +#ifdef IRS_LCL_SV_DB + if (pvt->dbh != NULL) + (*pvt->dbh->close)(pvt->dbh); +#endif + if (pvt->sv.fp) + fclose(pvt->sv.fp); + memput(pvt, sizeof *pvt); + memput(this, sizeof *this); +} + +static struct servent * +sv_byname(struct irs_sv *this, const char *name, const char *proto) { +#ifdef IRS_LCL_SV_DB + struct pvt *pvt = (struct pvt *)this->private; +#endif + struct servent *p; + char **cp; + + sv_rewind(this); +#ifdef IRS_LCL_SV_DB + if (pvt->dbh != NULL) { + DBT key, data; + + /* Note that (sizeof "/") == 2. */ + if ((strlen(name) + sizeof "/" + proto ? strlen(proto) : 0) + > sizeof pvt->sv.line) + goto try_local; + key.data = pvt->sv.line; + key.size = SPRINTF((pvt->sv.line, "%s/%s", name, + proto ? proto : "")) + 1; + if (proto != NULL) { + if ((*pvt->dbh->get)(pvt->dbh, &key, &data, 0) != 0) + return (NULL); + } else if ((*pvt->dbh->seq)(pvt->dbh, &key, &data, R_CURSOR) + != 0) + return (NULL); + return (sv_db_rec(&pvt->sv, &key, &data)); + } + try_local: +#endif + + while ((p = sv_next(this))) { + if (strcmp(name, p->s_name) == 0) + goto gotname; + for (cp = p->s_aliases; *cp; cp++) + if (strcmp(name, *cp) == 0) + goto gotname; + continue; + gotname: + if (proto == NULL || strcmp(p->s_proto, proto) == 0) + break; + } + return (p); +} + +static struct servent * +sv_byport(struct irs_sv *this, int port, const char *proto) { +#ifdef IRS_LCL_SV_DB + struct pvt *pvt = (struct pvt *)this->private; +#endif + struct servent *p; + + sv_rewind(this); +#ifdef IRS_LCL_SV_DB + if (pvt->dbh != NULL) { + DBT key, data; + u_short *ports; + + ports = (u_short *)pvt->sv.line; + ports[0] = 0; + ports[1] = port; + key.data = ports; + key.size = sizeof(u_short) * 2; + if (proto && *proto) { + strncpy((char *)ports + key.size, proto, + BUFSIZ - key.size); + key.size += strlen((char *)ports + key.size) + 1; + if ((*pvt->dbh->get)(pvt->dbh, &key, &data, 0) != 0) + return (NULL); + } else { + if ((*pvt->dbh->seq)(pvt->dbh, &key, &data, R_CURSOR) + != 0) + return (NULL); + } + return (sv_db_rec(&pvt->sv, &key, &data)); + } +#endif + while ((p = sv_next(this))) { + if (p->s_port != port) + continue; + if (proto == NULL || strcmp(p->s_proto, proto) == 0) + break; + } + return (p); +} + +static void +sv_rewind(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + + if (pvt->sv.fp) { + if (fseek(pvt->sv.fp, 0L, SEEK_SET) == 0) + return; + (void)fclose(pvt->sv.fp); + pvt->sv.fp = NULL; + } +#ifdef IRS_LCL_SV_DB + pvt->dbf = R_FIRST; + if (pvt->dbh != NULL) + return; + pvt->dbh = dbopen(_PATH_SERVICES_DB, O_RDONLY,O_RDONLY,DB_BTREE, NULL); + if (pvt->dbh != NULL) { + if (fcntl((*pvt->dbh->fd)(pvt->dbh), F_SETFD, 1) < 0) { + (*pvt->dbh->close)(pvt->dbh); + pvt->dbh = NULL; + } + return; + } +#endif + if ((pvt->sv.fp = fopen(_PATH_SERVICES, "r")) == NULL) + return; + if (fcntl(fileno(pvt->sv.fp), F_SETFD, 1) < 0) { + (void)fclose(pvt->sv.fp); + pvt->sv.fp = NULL; + } +} + +static struct servent * +sv_next(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + +#ifdef IRS_LCL_SV_DB + if (pvt->dbh == NULL && pvt->sv.fp == NULL) +#else + if (pvt->sv.fp == NULL) +#endif + sv_rewind(this); + +#ifdef IRS_LCL_SV_DB + if (pvt->dbh != NULL) { + DBT key, data; + + while ((*pvt->dbh->seq)(pvt->dbh, &key, &data, pvt->dbf) == 0){ + pvt->dbf = R_NEXT; + if (((char *)key.data)[0]) + continue; + return (sv_db_rec(&pvt->sv, &key, &data)); + } + } +#endif + + if (pvt->sv.fp == NULL) + return (NULL); + return (irs_lclsv_fnxt(&pvt->sv)); +} + +static void +sv_minimize(struct irs_sv *this) { + struct pvt *pvt = (struct pvt *)this->private; + +#ifdef IRS_LCL_SV_DB + if (pvt->dbh != NULL) { + (*pvt->dbh->close)(pvt->dbh); + pvt->dbh = NULL; + } +#endif + if (pvt->sv.fp != NULL) { + (void)fclose(pvt->sv.fp); + pvt->sv.fp = NULL; + } +} + +/* Quasipublic. */ + +struct servent * +irs_lclsv_fnxt(struct lcl_sv *sv) { + char *p, *cp, **q; + + again: + if ((p = fgets(sv->line, BUFSIZ, sv->fp)) == NULL) + return (NULL); + if (*p == '#') + goto again; + sv->serv.s_name = p; + while (*p && *p != '\n' && *p != ' ' && *p != '\t' && *p != '#') + ++p; + if (*p == '\0' || *p == '#' || *p == '\n') + goto again; + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + if (*p == '\0' || *p == '#' || *p == '\n') + goto again; + sv->serv.s_port = htons((u_short)strtol(p, &cp, 10)); + if (cp == p || (*cp != '/' && *cp != ',')) + goto again; + p = cp + 1; + sv->serv.s_proto = p; + + q = sv->serv.s_aliases = sv->serv_aliases; + + while (*p && *p != '\n' && *p != ' ' && *p != '\t' && *p != '#') + ++p; + + while (*p == ' ' || *p == '\t') { + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + ++p; + if (*p == '\0' || *p == '#' || *p == '\n') + break; + if (q < &sv->serv_aliases[IRS_SV_MAXALIASES - 1]) + *q++ = p; + while (*p && *p != '\n' && *p != ' ' && *p != '\t' && *p != '#') + ++p; + } + + *p = '\0'; + *q = NULL; + return (&sv->serv); +} + +/* Private. */ + +#ifdef IRS_LCL_SV_DB +static struct servent * +sv_db_rec(struct lcl_sv *sv, DBT *key, DBT *data) { + char *p, **q; + int n; + + p = data->data; + p[data->size - 1] = '\0'; /*%< should be, but we depend on it */ + if (((char *)key->data)[0] == '\0') { + if (key->size < sizeof(u_short)*2 || data->size < 2) + return (NULL); + sv->serv.s_port = ((u_short *)key->data)[1]; + n = strlen(p) + 1; + if ((size_t)n > sizeof(sv->line)) { + n = sizeof(sv->line); + } + memcpy(sv->line, p, n); + sv->serv.s_name = sv->line; + if ((sv->serv.s_proto = strchr(sv->line, '/')) != NULL) + *(sv->serv.s_proto)++ = '\0'; + p += n; + data->size -= n; + } else { + if (data->size < sizeof(u_short) + 1) + return (NULL); + if (key->size > sizeof(sv->line)) + key->size = sizeof(sv->line); + ((char *)key->data)[key->size - 1] = '\0'; + memcpy(sv->line, key->data, key->size); + sv->serv.s_name = sv->line; + if ((sv->serv.s_proto = strchr(sv->line, '/')) != NULL) + *(sv->serv.s_proto)++ = '\0'; + sv->serv.s_port = *(u_short *)data->data; + p += sizeof(u_short); + data->size -= sizeof(u_short); + } + q = sv->serv.s_aliases = sv->serv_aliases; + while (data->size > 0 && q < &sv->serv_aliases[IRS_SV_MAXALIASES - 1]) { + + *q++ = p; + n = strlen(p) + 1; + data->size -= n; + p += n; + } + *q = NULL; + return (&sv->serv); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/nis.c b/usr/src/lib/libresolv2_joy/common/irs/nis.c new file mode 100644 index 0000000000..0325c47a1c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/nis.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#ifdef WANT_IRS_NIS + +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <sys/types.h> +#include <netinet/in.h> +#ifdef T_NULL +#undef T_NULL /* Silence re-definition warning of T_NULL. */ +#endif +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <isc/memcluster.h> +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "hesiod.h" +#include "nis_p.h" + +/* Forward */ + +static void nis_close(struct irs_acc *); +static struct __res_state * nis_res_get(struct irs_acc *); +static void nis_res_set(struct irs_acc *, struct __res_state *, + void (*)(void *)); + +/* Public */ + +struct irs_acc * +irs_nis_acc(const char *options) { + struct nis_p *nis; + struct irs_acc *acc; + char *domain; + + UNUSED(options); + + if (yp_get_default_domain(&domain) != 0) + return (NULL); + if (!(nis = memget(sizeof *nis))) { + errno = ENOMEM; + return (NULL); + } + memset(nis, 0, sizeof *nis); + if (!(acc = memget(sizeof *acc))) { + memput(nis, sizeof *nis); + errno = ENOMEM; + return (NULL); + } + memset(acc, 0x5e, sizeof *acc); + acc->private = nis; + nis->domain = strdup(domain); +#ifdef WANT_IRS_GR + acc->gr_map = irs_nis_gr; +#else + acc->gr_map = NULL; +#endif +#ifdef WANT_IRS_PW + acc->pw_map = irs_nis_pw; +#else + acc->pw_map = NULL; +#endif + acc->sv_map = irs_nis_sv; + acc->pr_map = irs_nis_pr; + acc->ho_map = irs_nis_ho; + acc->nw_map = irs_nis_nw; + acc->ng_map = irs_nis_ng; + acc->res_get = nis_res_get; + acc->res_set = nis_res_set; + acc->close = nis_close; + return (acc); +} + +/* Methods */ + +static struct __res_state * +nis_res_get(struct irs_acc *this) { + struct nis_p *nis = (struct nis_p *)this->private; + + if (nis->res == NULL) { + struct __res_state *res; + res = (struct __res_state *)malloc(sizeof *res); + if (res == NULL) + return (NULL); + memset(res, 0, sizeof *res); + nis_res_set(this, res, free); + } + + if ((nis->res->options & RES_INIT) == 0 && + res_ninit(nis->res) < 0) + return (NULL); + + return (nis->res); +} + +static void +nis_res_set(struct irs_acc *this, struct __res_state *res, + void (*free_res)(void *)) { + struct nis_p *nis = (struct nis_p *)this->private; + + if (nis->res && nis->free_res) { + res_nclose(nis->res); + (*nis->free_res)(nis->res); + } + + nis->res = res; + nis->free_res = free_res; +} + +static void +nis_close(struct irs_acc *this) { + struct nis_p *nis = (struct nis_p *)this->private; + + if (nis->res && nis->free_res) + (*nis->free_res)(nis->res); + free(nis->domain); + memput(nis, sizeof *nis); + memput(this, sizeof *this); +} + +#endif /*WANT_IRS_NIS*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/nis_p.h b/usr/src/lib/libresolv2_joy/common/irs/nis_p.h new file mode 100644 index 0000000000..70e2948d67 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/nis_p.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: nis_p.h,v 1.3 2005/04/27 04:56:33 sra Exp $ + */ + +/*! \file + * \brief + * nis_p.h - private include file for the NIS functions. + */ + +/*% + * Object state. + */ +struct nis_p { + char * domain; + struct __res_state * res; + void (*free_res) __P((void *)); +}; + + +/* + * Methods. + */ + +extern struct irs_gr * irs_nis_gr __P((struct irs_acc *)); +extern struct irs_pw * irs_nis_pw __P((struct irs_acc *)); +extern struct irs_sv * irs_nis_sv __P((struct irs_acc *)); +extern struct irs_pr * irs_nis_pr __P((struct irs_acc *)); +extern struct irs_ho * irs_nis_ho __P((struct irs_acc *)); +extern struct irs_nw * irs_nis_nw __P((struct irs_acc *)); +extern struct irs_ng * irs_nis_ng __P((struct irs_acc *)); diff --git a/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c b/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c new file mode 100644 index 0000000000..a16ff9c2f0 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief + * nul_ng.c - the netgroup accessor null map + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> + +#include <irs.h> +#include <isc/memcluster.h> + +#include "port_after.h" + +#include "irs_p.h" +#include "hesiod.h" +#include "dns_p.h" + +/* Forward. */ + +static void ng_close(struct irs_ng *); +static int ng_next(struct irs_ng *, const char **, + const char **, const char **); +static int ng_test(struct irs_ng *, + const char *, const char *, + const char *, const char *); +static void ng_rewind(struct irs_ng *, const char *); +static void ng_minimize(struct irs_ng *); + +/* Public. */ + +struct irs_ng * +irs_nul_ng(struct irs_acc *this) { + struct irs_ng *ng; + + UNUSED(this); + + if (!(ng = memget(sizeof *ng))) { + errno = ENOMEM; + return (NULL); + } + memset(ng, 0x5e, sizeof *ng); + ng->private = NULL; + ng->close = ng_close; + ng->next = ng_next; + ng->test = ng_test; + ng->rewind = ng_rewind; + ng->minimize = ng_minimize; + return (ng); +} + +/* Methods. */ + +static void +ng_close(struct irs_ng *this) { + memput(this, sizeof *this); +} + +/* ARGSUSED */ +static int +ng_next(struct irs_ng *this, const char **host, const char **user, + const char **domain) +{ + UNUSED(this); + UNUSED(host); + UNUSED(user); + UNUSED(domain); + errno = ENOENT; + return (-1); +} + +static int +ng_test(struct irs_ng *this, const char *name, + const char *user, const char *host, const char *domain) +{ + UNUSED(this); + UNUSED(name); + UNUSED(user); + UNUSED(host); + UNUSED(domain); + errno = ENODEV; + return (-1); +} + +static void +ng_rewind(struct irs_ng *this, const char *netgroup) { + UNUSED(this); + UNUSED(netgroup); + /* NOOP */ +} + +static void +ng_minimize(struct irs_ng *this) { + UNUSED(this); + /* NOOP */ +} diff --git a/usr/src/lib/libresolv2_joy/common/irs/pathnames.h b/usr/src/lib/libresolv2_joy/common/irs/pathnames.h new file mode 100644 index 0000000000..1646842155 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/pathnames.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: pathnames.h,v 1.3 2005/04/27 04:56:34 sra Exp $ + */ + +#ifndef _PATH_IRS_CONF +#define _PATH_IRS_CONF "/etc/irs.conf" +#endif + +#ifndef _PATH_NETWORKS +#define _PATH_NETWORKS "/etc/networks" +#endif + +#ifndef _PATH_GROUP +#define _PATH_GROUP "/etc/group" +#endif + +#ifndef _PATH_NETGROUP +#define _PATH_NETGROUP "/etc/netgroup" +#endif + +#ifndef _PATH_SERVICES +#define _PATH_SERVICES "/etc/services" +#endif + +#ifdef IRS_LCL_SV_DB +#ifndef _PATH_SERVICES_DB +#define _PATH_SERVICES_DB _PATH_SERVICES ".db" +#endif +#endif + +#ifndef _PATH_HESIOD_CONF +#define _PATH_HESIOD_CONF "/etc/hesiod.conf" +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/irs/util.c b/usr/src/lib/libresolv2_joy/common/irs/util.c new file mode 100644 index 0000000000..4afc530fb0 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/util.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <irs.h> + +#include "port_after.h" + +#include "irs_p.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) sprintf x +#endif + +void +map_v4v6_address(const char *src, char *dst) { + u_char *p = (u_char *)dst; + char tmp[NS_INADDRSZ]; + int i; + + /* Stash a temporary copy so our caller can update in place. */ + memcpy(tmp, src, NS_INADDRSZ); + /* Mark this ipv6 addr as a mapped ipv4. */ + for (i = 0; i < 10; i++) + *p++ = 0x00; + *p++ = 0xff; + *p++ = 0xff; + /* Retrieve the saved copy and we're done. */ + memcpy((void*)p, tmp, NS_INADDRSZ); +} + +int +make_group_list(struct irs_gr *this, const char *name, + gid_t basegid, gid_t *groups, int *ngroups) +{ + struct group *grp; + int i, ng; + int ret, maxgroups; + + ret = -1; + ng = 0; + maxgroups = *ngroups; + /* + * When installing primary group, duplicate it; + * the first element of groups is the effective gid + * and will be overwritten when a setgid file is executed. + */ + if (ng >= maxgroups) + goto done; + groups[ng++] = basegid; + if (ng >= maxgroups) + goto done; + groups[ng++] = basegid; + /* + * Scan the group file to find additional groups. + */ + (*this->rewind)(this); + while ((grp = (*this->next)(this)) != NULL) { + if ((gid_t)grp->gr_gid == basegid) + continue; + for (i = 0; grp->gr_mem[i]; i++) { + if (!strcmp(grp->gr_mem[i], name)) { + if (ng >= maxgroups) + goto done; + groups[ng++] = grp->gr_gid; + break; + } + } + } + ret = 0; + done: + *ngroups = ng; + return (ret); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/assertions.c b/usr/src/lib/libresolv2_joy/common/isc/assertions.c new file mode 100644 index 0000000000..6516d66cc8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/assertions.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1997, 1999, 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/assertions.h> + +#include "port_after.h" + +/* + * Forward. + */ + +static void default_assertion_failed(const char *, int, assertion_type, + const char *, int); + +/* + * Public. + */ + +assertion_failure_callback __assertion_failed = default_assertion_failed; + +void +set_assertion_failure_callback(assertion_failure_callback f) { + if (f == NULL) + __assertion_failed = default_assertion_failed; + else + __assertion_failed = f; +} + +const char * +assertion_type_to_text(assertion_type type) { + const char *result; + + switch (type) { + case assert_require: + result = "REQUIRE"; + break; + case assert_ensure: + result = "ENSURE"; + break; + case assert_insist: + result = "INSIST"; + break; + case assert_invariant: + result = "INVARIANT"; + break; + default: + result = NULL; + } + return (result); +} + +/* + * Private. + */ + +/* coverity[+kill] */ +static void +default_assertion_failed(const char *file, int line, assertion_type type, + const char *cond, int print_errno) +{ + fprintf(stderr, "%s:%d: %s(%s)%s%s failed.\n", + file, line, assertion_type_to_text(type), cond, + (print_errno) ? ": " : "", + (print_errno) ? strerror(errno) : ""); + abort(); + /* NOTREACHED */ +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/base64.c b/usr/src/lib/libresolv2_joy/common/isc/base64.c new file mode 100644 index 0000000000..a819fd97bf --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/base64.c @@ -0,0 +1,329 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "port_after.h" + +#ifndef ORIGINAL_ISC_CODE +#pragma weak __b64_ntop = b64_ntop +#pragma weak __b64_pton = b64_pton +#endif /* ORIGINAL_ISC_CODE */ + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2U < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0U != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1U) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /*%< Returned value doesn't count \\0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(src, target, targsize) + char const *src; + u_char *target; + size_t targsize; +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /*%< Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /*%< A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /*%< We got a pad char. */ + ch = *src++; /*%< Skip it, get next. */ + switch (state) { + case 0: /*%< Invalid = in first position */ + case 1: /*%< Invalid = in second position */ + return (-1); + + case 2: /*%< Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /*%< Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /*%< Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c b/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c new file mode 100644 index 0000000000..d229e04c89 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1996, 1999, 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <string.h> + +#include "port_after.h" + +#include <isc/misc.h> + +/*% + * int + * bitncmp(l, r, n) + * compare bit masks l and r, for n bits. + * return: + * -1, 1, or 0 in the libc tradition. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +bitncmp(const void *l, const void *r, int n) { + u_int lb, rb; + int x, b; + + b = n / 8; + x = memcmp(l, r, b); + if (x || (n % 8) == 0) + return (x); + + lb = ((const u_char *)l)[b]; + rb = ((const u_char *)r)[b]; + for (b = n % 8; b > 0; b--) { + if ((lb & 0x80) != (rb & 0x80)) { + if (lb & 0x80) + return (1); + return (-1); + } + lb <<= 1; + rb <<= 1; + } + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c b/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c new file mode 100644 index 0000000000..eca06b2ee2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1998-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +#include <isc/assertions.h> +#include <isc/ctl.h> +#include <isc/eventlib.h> +#include <isc/list.h> +#include <isc/memcluster.h> + +#include "ctl_p.h" + +#include "port_after.h" + +/* Constants. */ + + +/* Macros. */ + +#define donefunc_p(ctx) ((ctx).donefunc != NULL) +#define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \ + isdigit((unsigned char)(line[1])) && \ + isdigit((unsigned char)(line[2]))) +#define arpacont_p(line) (line[3] == '-') +#define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \ + line[3] == '\r' || line[3] == '\0') + +/* Types. */ + +enum state { + initializing = 0, connecting, connected, destroyed +}; + +struct ctl_tran { + LINK(struct ctl_tran) link; + LINK(struct ctl_tran) wlink; + struct ctl_cctx * ctx; + struct ctl_buf outbuf; + ctl_clntdone donefunc; + void * uap; +}; + +struct ctl_cctx { + enum state state; + evContext ev; + int sock; + ctl_logfunc logger; + ctl_clntdone donefunc; + void * uap; + evConnID coID; + evTimerID tiID; + evFileID rdID; + evStreamID wrID; + struct ctl_buf inbuf; + struct timespec timeout; + LIST(struct ctl_tran) tran; + LIST(struct ctl_tran) wtran; +}; + +/* Forward. */ + +static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int); +static void start_write(struct ctl_cctx *); +static void destroy(struct ctl_cctx *, int); +static void error(struct ctl_cctx *); +static void new_state(struct ctl_cctx *, enum state); +static void conn_done(evContext, void *, int, + const void *, int, + const void *, int); +static void write_done(evContext, void *, int, int); +static void start_read(struct ctl_cctx *); +static void stop_read(struct ctl_cctx *); +static void readable(evContext, void *, int, int); +static void start_timer(struct ctl_cctx *); +static void stop_timer(struct ctl_cctx *); +static void touch_timer(struct ctl_cctx *); +static void timer(evContext, void *, + struct timespec, struct timespec); + +#ifndef HAVE_MEMCHR +static void * +memchr(const void *b, int c, size_t len) { + const unsigned char *p = b; + size_t i; + + for (i = 0; i < len; i++, p++) + if (*p == (unsigned char)c) + return ((void *)p); + return (NULL); +} +#endif + +/* Private data. */ + +static const char * const state_names[] = { + "initializing", "connecting", "connected", "destroyed" +}; + +/* Public. */ + +/*% + * void + * ctl_client() + * create, condition, and connect to a listener on the control port. + */ +struct ctl_cctx * +ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len, + const struct sockaddr *sap, size_t sap_len, + ctl_clntdone donefunc, void *uap, + u_int timeout, ctl_logfunc logger) +{ + static const char me[] = "ctl_client"; + static const int on = 1; + struct ctl_cctx *ctx; + struct sockaddr *captmp; + + if (logger == NULL) + logger = ctl_logger; + ctx = memget(sizeof *ctx); + if (ctx == NULL) { + (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); + goto fatal; + } + ctx->state = initializing; + ctx->ev = lev; + ctx->logger = logger; + ctx->timeout = evConsTime(timeout, 0); + ctx->donefunc = donefunc; + ctx->uap = uap; + ctx->coID.opaque = NULL; + ctx->tiID.opaque = NULL; + ctx->rdID.opaque = NULL; + ctx->wrID.opaque = NULL; + buffer_init(ctx->inbuf); + INIT_LIST(ctx->tran); + INIT_LIST(ctx->wtran); + ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); + if (ctx->sock > evHighestFD(ctx->ev)) { + ctx->sock = -1; + errno = ENOTSOCK; + } + if (ctx->sock < 0) { + (*ctx->logger)(ctl_error, "%s: socket: %s", + me, strerror(errno)); + goto fatal; + } + if (cap != NULL) { + if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&on, sizeof on) != 0) { + (*ctx->logger)(ctl_warning, + "%s: setsockopt(REUSEADDR): %s", + me, strerror(errno)); + } + DE_CONST(cap, captmp); + if (bind(ctx->sock, captmp, cap_len) < 0) { + (*ctx->logger)(ctl_error, "%s: bind: %s", me, + strerror(errno)); + goto fatal; + } + } + if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len, + conn_done, ctx, &ctx->coID) < 0) { + (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s", + me, ctx->sock, strerror(errno)); + fatal: + if (ctx != NULL) { + if (ctx->sock >= 0) + close(ctx->sock); + memput(ctx, sizeof *ctx); + } + return (NULL); + } + new_state(ctx, connecting); + return (ctx); +} + +/*% + * void + * ctl_endclient(ctx) + * close a client and release all of its resources. + */ +void +ctl_endclient(struct ctl_cctx *ctx) { + if (ctx->state != destroyed) + destroy(ctx, 0); + memput(ctx, sizeof *ctx); +} + +/*% + * int + * ctl_command(ctx, cmd, len, donefunc, uap) + * Queue a transaction, which will begin with sending cmd + * and complete by calling donefunc with the answer. + */ +int +ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len, + ctl_clntdone donefunc, void *uap) +{ + struct ctl_tran *tran; + char *pc; + unsigned int n; + + switch (ctx->state) { + case destroyed: + errno = ENOTCONN; + return (-1); + case connecting: + case connected: + break; + default: + abort(); + } + if (len >= (size_t)MAX_LINELEN) { + errno = EMSGSIZE; + return (-1); + } + tran = new_tran(ctx, donefunc, uap, 1); + if (tran == NULL) + return (-1); + if (ctl_bufget(&tran->outbuf, ctx->logger) < 0) + return (-1); + memcpy(tran->outbuf.text, cmd, len); + tran->outbuf.used = len; + for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++) + if (!isascii((unsigned char)*pc) || + !isprint((unsigned char)*pc)) + *pc = '\040'; + start_write(ctx); + return (0); +} + +/* Private. */ + +static struct ctl_tran * +new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) { + struct ctl_tran *new = memget(sizeof *new); + + if (new == NULL) + return (NULL); + new->ctx = ctx; + buffer_init(new->outbuf); + new->donefunc = donefunc; + new->uap = uap; + INIT_LINK(new, link); + INIT_LINK(new, wlink); + APPEND(ctx->tran, new, link); + if (w) + APPEND(ctx->wtran, new, wlink); + return (new); +} + +static void +start_write(struct ctl_cctx *ctx) { + static const char me[] = "isc/ctl_clnt::start_write"; + struct ctl_tran *tran; + struct iovec iov[2], *iovp = iov; + char * tmp; + + REQUIRE(ctx->state == connecting || ctx->state == connected); + /* If there is a write in progress, don't try to write more yet. */ + if (ctx->wrID.opaque != NULL) + return; + /* If there are no trans, make sure timer is off, and we're done. */ + if (EMPTY(ctx->wtran)) { + if (ctx->tiID.opaque != NULL) + stop_timer(ctx); + return; + } + /* Pull it off the head of the write queue. */ + tran = HEAD(ctx->wtran); + UNLINK(ctx->wtran, tran, wlink); + /* Since there are some trans, make sure timer is successfully "on". */ + if (ctx->tiID.opaque != NULL) + touch_timer(ctx); + else + start_timer(ctx); + if (ctx->state == destroyed) + return; + /* Marshall a newline-terminated message and clock it out. */ + *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used); + DE_CONST("\r\n", tmp); + *iovp++ = evConsIovec(tmp, 2); + if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov, + write_done, tran, &ctx->wrID) < 0) { + (*ctx->logger)(ctl_error, "%s: evWrite: %s", me, + strerror(errno)); + error(ctx); + return; + } + if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) { + (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me, + strerror(errno)); + error(ctx); + return; + } +} + +static void +destroy(struct ctl_cctx *ctx, int notify) { + struct ctl_tran *this, *next; + + if (ctx->sock != -1) { + (void) close(ctx->sock); + ctx->sock = -1; + } + switch (ctx->state) { + case connecting: + REQUIRE(ctx->wrID.opaque == NULL); + REQUIRE(EMPTY(ctx->tran)); + /* + * This test is nec'y since destroy() can be called from + * start_read() while the state is still "connecting". + */ + if (ctx->coID.opaque != NULL) { + (void)evCancelConn(ctx->ev, ctx->coID); + ctx->coID.opaque = NULL; + } + break; + case connected: + REQUIRE(ctx->coID.opaque == NULL); + if (ctx->wrID.opaque != NULL) { + (void)evCancelRW(ctx->ev, ctx->wrID); + ctx->wrID.opaque = NULL; + } + if (ctx->rdID.opaque != NULL) + stop_read(ctx); + break; + case destroyed: + break; + default: + abort(); + } + if (allocated_p(ctx->inbuf)) + ctl_bufput(&ctx->inbuf); + for (this = HEAD(ctx->tran); this != NULL; this = next) { + next = NEXT(this, link); + if (allocated_p(this->outbuf)) + ctl_bufput(&this->outbuf); + if (notify && this->donefunc != NULL) + (*this->donefunc)(ctx, this->uap, NULL, 0); + memput(this, sizeof *this); + } + if (ctx->tiID.opaque != NULL) + stop_timer(ctx); + new_state(ctx, destroyed); +} + +static void +error(struct ctl_cctx *ctx) { + REQUIRE(ctx->state != destroyed); + destroy(ctx, 1); +} + +static void +new_state(struct ctl_cctx *ctx, enum state new_state) { + static const char me[] = "isc/ctl_clnt::new_state"; + + (*ctx->logger)(ctl_debug, "%s: %s -> %s", me, + state_names[ctx->state], state_names[new_state]); + ctx->state = new_state; +} + +static void +conn_done(evContext ev, void *uap, int fd, + const void *la, int lalen, + const void *ra, int ralen) +{ + static const char me[] = "isc/ctl_clnt::conn_done"; + struct ctl_cctx *ctx = uap; + struct ctl_tran *tran; + + UNUSED(ev); + UNUSED(la); + UNUSED(lalen); + UNUSED(ra); + UNUSED(ralen); + + ctx->coID.opaque = NULL; + if (fd < 0) { + (*ctx->logger)(ctl_error, "%s: evConnect: %s", me, + strerror(errno)); + error(ctx); + return; + } + new_state(ctx, connected); + tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0); + if (tran == NULL) { + (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me, + strerror(errno)); + error(ctx); + return; + } + start_read(ctx); + if (ctx->state == destroyed) { + (*ctx->logger)(ctl_error, "%s: start_read failed: %s", + me, strerror(errno)); + error(ctx); + return; + } +} + +static void +write_done(evContext lev, void *uap, int fd, int bytes) { + struct ctl_tran *tran = (struct ctl_tran *)uap; + struct ctl_cctx *ctx = tran->ctx; + + UNUSED(lev); + UNUSED(fd); + + ctx->wrID.opaque = NULL; + if (ctx->tiID.opaque != NULL) + touch_timer(ctx); + ctl_bufput(&tran->outbuf); + start_write(ctx); + if (bytes < 0) + destroy(ctx, 1); + else + start_read(ctx); +} + +static void +start_read(struct ctl_cctx *ctx) { + static const char me[] = "isc/ctl_clnt::start_read"; + + REQUIRE(ctx->state == connecting || ctx->state == connected); + REQUIRE(ctx->rdID.opaque == NULL); + if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx, + &ctx->rdID) < 0) + { + (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me, + ctx->sock, strerror(errno)); + error(ctx); + return; + } +} + +static void +stop_read(struct ctl_cctx *ctx) { + REQUIRE(ctx->coID.opaque == NULL); + REQUIRE(ctx->rdID.opaque != NULL); + (void)evDeselectFD(ctx->ev, ctx->rdID); + ctx->rdID.opaque = NULL; +} + +static void +readable(evContext ev, void *uap, int fd, int evmask) { + static const char me[] = "isc/ctl_clnt::readable"; + struct ctl_cctx *ctx = uap; + struct ctl_tran *tran; + ssize_t n; + char *eos; + + UNUSED(ev); + + REQUIRE(ctx != NULL); + REQUIRE(fd >= 0); + REQUIRE(evmask == EV_READ); + REQUIRE(ctx->state == connected); + REQUIRE(!EMPTY(ctx->tran)); + tran = HEAD(ctx->tran); + if (!allocated_p(ctx->inbuf) && + ctl_bufget(&ctx->inbuf, ctx->logger) < 0) { + (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me); + error(ctx); + return; + } + n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used, + MAX_LINELEN - ctx->inbuf.used); + if (n <= 0) { + (*ctx->logger)(ctl_warning, "%s: read: %s", me, + (n == 0) ? "Unexpected EOF" : strerror(errno)); + error(ctx); + return; + } + if (ctx->tiID.opaque != NULL) + touch_timer(ctx); + ctx->inbuf.used += n; + (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me, + n, ctx->inbuf.used); + again: + eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used); + if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') { + int done = 0; + + eos[-1] = '\0'; + if (!arpacode_p(ctx->inbuf.text)) { + /* XXX Doesn't FTP do this sometimes? Is it legal? */ + (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me, + ctx->inbuf.text); + error(ctx); + return; + } + if (arpadone_p(ctx->inbuf.text)) + done = 1; + else if (arpacont_p(ctx->inbuf.text)) + done = 0; + else { + /* XXX Doesn't FTP do this sometimes? Is it legal? */ + (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me, + ctx->inbuf.text); + error(ctx); + return; + } + (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text, + (done ? 0 : CTL_MORE)); + ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1); + if (ctx->inbuf.used == 0U) + ctl_bufput(&ctx->inbuf); + else + memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used); + if (done) { + UNLINK(ctx->tran, tran, link); + memput(tran, sizeof *tran); + stop_read(ctx); + start_write(ctx); + return; + } + if (allocated_p(ctx->inbuf)) + goto again; + return; + } + if (ctx->inbuf.used == (size_t)MAX_LINELEN) { + (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me, + ctx->inbuf.text); + error(ctx); + } +} + +/* Timer related stuff. */ + +static void +start_timer(struct ctl_cctx *ctx) { + static const char me[] = "isc/ctl_clnt::start_timer"; + + REQUIRE(ctx->tiID.opaque == NULL); + if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){ + (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me, + strerror(errno)); + error(ctx); + return; + } +} + +static void +stop_timer(struct ctl_cctx *ctx) { + static const char me[] = "isc/ctl_clnt::stop_timer"; + + REQUIRE(ctx->tiID.opaque != NULL); + if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) { + (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me, + strerror(errno)); + error(ctx); + return; + } + ctx->tiID.opaque = NULL; +} + +static void +touch_timer(struct ctl_cctx *ctx) { + REQUIRE(ctx->tiID.opaque != NULL); + + evTouchIdleTimer(ctx->ev, ctx->tiID); +} + +static void +timer(evContext ev, void *uap, struct timespec due, struct timespec itv) { + static const char me[] = "isc/ctl_clnt::timer"; + struct ctl_cctx *ctx = uap; + + UNUSED(ev); + UNUSED(due); + UNUSED(itv); + + ctx->tiID.opaque = NULL; + (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me, + ctx->timeout.tv_sec, state_names[ctx->state]); + error(ctx); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c new file mode 100644 index 0000000000..3c0ebf4037 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Extern. */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <isc/assertions.h> +#include <isc/eventlib.h> +#include <isc/logging.h> +#include <isc/memcluster.h> +#include <isc/ctl.h> + +#include "ctl_p.h" + +#include "port_after.h" + +/* Constants. */ + +const char * const ctl_sevnames[] = { + "debug", "warning", "error" +}; + +/* Public. */ + +/*% + * ctl_logger() + * if ctl_startup()'s caller didn't specify a logger, this one + * is used. this pollutes stderr with all kinds of trash so it will + * probably never be used in real applications. + */ +void +ctl_logger(enum ctl_severity severity, const char *format, ...) { + va_list ap; + static const char me[] = "ctl_logger"; + + fprintf(stderr, "%s(%s): ", me, ctl_sevnames[severity]); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fputc('\n', stderr); +} + +int +ctl_bufget(struct ctl_buf *buf, ctl_logfunc logger) { + static const char me[] = "ctl_bufget"; + + REQUIRE(!allocated_p(*buf) && buf->used == 0U); + buf->text = memget(MAX_LINELEN); + if (!allocated_p(*buf)) { + (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); + return (-1); + } + buf->used = 0; + return (0); +} + +void +ctl_bufput(struct ctl_buf *buf) { + + REQUIRE(allocated_p(*buf)); + memput(buf->text, MAX_LINELEN); + buf->text = NULL; + buf->used = 0; +} + +const char * +ctl_sa_ntop(const struct sockaddr *sa, + char *buf, size_t size, + ctl_logfunc logger) +{ + static const char me[] = "ctl_sa_ntop"; + static const char punt[] = "[0].-1"; + char tmp[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET6: { + const struct sockaddr_in6 *in6 = + (const struct sockaddr_in6 *) sa; + + if (inet_ntop(in6->sin6_family, &in6->sin6_addr, tmp, sizeof tmp) + == NULL) { + (*logger)(ctl_error, "%s: inet_ntop(%u %04x): %s", + me, in6->sin6_family, + in6->sin6_port, strerror(errno)); + return (punt); + } + if (strlen(tmp) + sizeof "[].65535" > size) { + (*logger)(ctl_error, "%s: buffer overflow", me); + return (punt); + } + (void) sprintf(buf, "[%s].%u", tmp, ntohs(in6->sin6_port)); + return (buf); + } + case AF_INET: { + const struct sockaddr_in *in = + (const struct sockaddr_in *) sa; + + if (inet_ntop(in->sin_family, &in->sin_addr, tmp, sizeof tmp) + == NULL) { + (*logger)(ctl_error, "%s: inet_ntop(%u %04x %08x): %s", + me, in->sin_family, + in->sin_port, in->sin_addr.s_addr, + strerror(errno)); + return (punt); + } + if (strlen(tmp) + sizeof "[].65535" > size) { + (*logger)(ctl_error, "%s: buffer overflow", me); + return (punt); + } + (void) sprintf(buf, "[%s].%u", tmp, ntohs(in->sin_port)); + return (buf); + } +#ifndef NO_SOCKADDR_UN + case AF_UNIX: { + const struct sockaddr_un *un = + (const struct sockaddr_un *) sa; + unsigned int x = sizeof un->sun_path; + + if (x > size) + x = size; + strncpy(buf, un->sun_path, x - 1); + buf[x - 1] = '\0'; + return (buf); + } +#endif + default: + return (punt); + } +} + +void +ctl_sa_copy(const struct sockaddr *src, struct sockaddr *dst) { + switch (src->sa_family) { + case AF_INET6: + *((struct sockaddr_in6 *)dst) = + *((const struct sockaddr_in6 *)src); + break; + case AF_INET: + *((struct sockaddr_in *)dst) = + *((const struct sockaddr_in *)src); + break; +#ifndef NO_SOCKADDR_UN + case AF_UNIX: + *((struct sockaddr_un *)dst) = + *((const struct sockaddr_un *)src); + break; +#endif + default: + *dst = *src; + break; + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_p.h b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.h new file mode 100644 index 0000000000..18a52ae39c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.h @@ -0,0 +1,28 @@ +struct ctl_buf { + char * text; + size_t used; +}; + +#define MAX_LINELEN 990 /*%< Like SMTP. */ +#ifndef NO_SOCKADDR_UN +#define MAX_NTOP PATH_MAX +#else +#define MAX_NTOP (sizeof "[255.255.255.255].65535") +#endif + +#define allocated_p(Buf) ((Buf).text != NULL) +#define buffer_init(Buf) ((Buf).text = 0, (Buf.used) = 0) + +#define ctl_bufget __ctl_bufget +#define ctl_bufput __ctl_bufput +#define ctl_sa_ntop __ctl_sa_ntop +#define ctl_sa_copy __ctl_sa_copy + +int ctl_bufget(struct ctl_buf *, ctl_logfunc); +void ctl_bufput(struct ctl_buf *); +const char * ctl_sa_ntop(const struct sockaddr *, char *, size_t, + ctl_logfunc); +void ctl_sa_copy(const struct sockaddr *, + struct sockaddr *); + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c b/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c new file mode 100644 index 0000000000..12191cf0b0 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c @@ -0,0 +1,781 @@ +/* + * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1998-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <fcntl.h> +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +#include <isc/assertions.h> +#include <isc/ctl.h> +#include <isc/eventlib.h> +#include <isc/list.h> +#include <isc/logging.h> +#include <isc/memcluster.h> + +#include "ctl_p.h" + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Macros. */ + +#define lastverb_p(verb) (verb->name == NULL || verb->func == NULL) +#define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \ + tmp, sizeof tmp, ctx->logger) + +/* Types. */ + +enum state { + available = 0, initializing, writing, reading, reading_data, + processing, idling, quitting, closing +}; + +union sa_un { + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif +}; + +struct ctl_sess { + LINK(struct ctl_sess) link; + struct ctl_sctx * ctx; + enum state state; + int sock; + union sa_un sa; + evFileID rdID; + evStreamID wrID; + evTimerID rdtiID; + evTimerID wrtiID; + struct ctl_buf inbuf; + struct ctl_buf outbuf; + const struct ctl_verb * verb; + u_int helpcode; + const void * respctx; + u_int respflags; + ctl_srvrdone donefunc; + void * uap; + void * csctx; +}; + +struct ctl_sctx { + evContext ev; + void * uctx; + u_int unkncode; + u_int timeoutcode; + const struct ctl_verb * verbs; + const struct ctl_verb * connverb; + int sock; + int max_sess; + int cur_sess; + struct timespec timeout; + ctl_logfunc logger; + evConnID acID; + LIST(struct ctl_sess) sess; +}; + +/* Forward. */ + +static void ctl_accept(evContext, void *, int, + const void *, int, + const void *, int); +static void ctl_close(struct ctl_sess *); +static void ctl_new_state(struct ctl_sess *, + enum state, + const char *); +static void ctl_start_read(struct ctl_sess *); +static void ctl_stop_read(struct ctl_sess *); +static void ctl_readable(evContext, void *, int, int); +static void ctl_rdtimeout(evContext, void *, + struct timespec, + struct timespec); +static void ctl_wrtimeout(evContext, void *, + struct timespec, + struct timespec); +static void ctl_docommand(struct ctl_sess *); +static void ctl_writedone(evContext, void *, int, int); +static void ctl_morehelp(struct ctl_sctx *, + struct ctl_sess *, + const struct ctl_verb *, + const char *, + u_int, const void *, void *); +static void ctl_signal_done(struct ctl_sctx *, + struct ctl_sess *); + +/* Private data. */ + +static const char * state_names[] = { + "available", "initializing", "writing", "reading", + "reading_data", "processing", "idling", "quitting", "closing" +}; + +static const char space[] = " "; + +static const struct ctl_verb fakehelpverb = { + "fakehelp", ctl_morehelp , NULL +}; + +/* Public. */ + +/*% + * void + * ctl_server() + * create, condition, and start a listener on the control port. + */ +struct ctl_sctx * +ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len, + const struct ctl_verb *verbs, + u_int unkncode, u_int timeoutcode, + u_int timeout, int backlog, int max_sess, + ctl_logfunc logger, void *uctx) +{ + static const char me[] = "ctl_server"; + static const int on = 1; + const struct ctl_verb *connverb; + struct ctl_sctx *ctx; + int save_errno; + + if (logger == NULL) + logger = ctl_logger; + for (connverb = verbs; + connverb->name != NULL && connverb->func != NULL; + connverb++) + if (connverb->name[0] == '\0') + break; + if (connverb->func == NULL) { + (*logger)(ctl_error, "%s: no connection verb found", me); + return (NULL); + } + ctx = memget(sizeof *ctx); + if (ctx == NULL) { + (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); + return (NULL); + } + ctx->ev = lev; + ctx->uctx = uctx; + ctx->unkncode = unkncode; + ctx->timeoutcode = timeoutcode; + ctx->verbs = verbs; + ctx->timeout = evConsTime(timeout, 0); + ctx->logger = logger; + ctx->connverb = connverb; + ctx->max_sess = max_sess; + ctx->cur_sess = 0; + INIT_LIST(ctx->sess); + ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); + if (ctx->sock > evHighestFD(ctx->ev)) { + ctx->sock = -1; + errno = ENOTSOCK; + } + if (ctx->sock < 0) { + save_errno = errno; + (*ctx->logger)(ctl_error, "%s: socket: %s", + me, strerror(errno)); + memput(ctx, sizeof *ctx); + errno = save_errno; + return (NULL); + } + if (ctx->sock > evHighestFD(lev)) { + close(ctx->sock); + (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD"); + errno = ENFILE; + memput(ctx, sizeof *ctx); + return (NULL); + } +#ifdef NO_UNIX_REUSEADDR + if (sap->sa_family != AF_UNIX) +#endif + if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&on, sizeof on) != 0) { + (*ctx->logger)(ctl_warning, + "%s: setsockopt(REUSEADDR): %s", + me, strerror(errno)); + } + if (bind(ctx->sock, sap, sap_len) < 0) { + char tmp[MAX_NTOP]; + save_errno = errno; + (*ctx->logger)(ctl_error, "%s: bind: %s: %s", + me, ctl_sa_ntop((const struct sockaddr *)sap, + tmp, sizeof tmp, ctx->logger), + strerror(save_errno)); + close(ctx->sock); + memput(ctx, sizeof *ctx); + errno = save_errno; + return (NULL); + } + if (fcntl(ctx->sock, F_SETFD, 1) < 0) { + (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, + strerror(errno)); + } + if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx, + &ctx->acID) < 0) { + save_errno = errno; + (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s", + me, ctx->sock, strerror(errno)); + close(ctx->sock); + memput(ctx, sizeof *ctx); + errno = save_errno; + return (NULL); + } + (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d", + me, ctx, ctx->sock); + return (ctx); +} + +/*% + * void + * ctl_endserver(ctx) + * if the control listener is open, close it. clean out all eventlib + * stuff. close all active sessions. + */ +void +ctl_endserver(struct ctl_sctx *ctx) { + static const char me[] = "ctl_endserver"; + struct ctl_sess *this, *next; + + (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p", + me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess); + if (ctx->acID.opaque != NULL) { + (void)evCancelConn(ctx->ev, ctx->acID); + ctx->acID.opaque = NULL; + } + if (ctx->sock != -1) { + (void) close(ctx->sock); + ctx->sock = -1; + } + for (this = HEAD(ctx->sess); this != NULL; this = next) { + next = NEXT(this, link); + ctl_close(this); + } + memput(ctx, sizeof *ctx); +} + +/*% + * If body is non-NULL then it we add a "." line after it. + * Caller must have escaped lines with leading ".". + */ +void +ctl_response(struct ctl_sess *sess, u_int code, const char *text, + u_int flags, const void *respctx, ctl_srvrdone donefunc, + void *uap, const char *body, size_t bodylen) +{ + static const char me[] = "ctl_response"; + struct iovec iov[3], *iovp = iov; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP], *pc; + int n; + + REQUIRE(sess->state == initializing || + sess->state == processing || + sess->state == reading_data || + sess->state == writing); + REQUIRE(sess->wrtiID.opaque == NULL); + REQUIRE(sess->wrID.opaque == NULL); + ctl_new_state(sess, writing, me); + sess->donefunc = donefunc; + sess->uap = uap; + if (!allocated_p(sess->outbuf) && + ctl_bufget(&sess->outbuf, ctx->logger) < 0) { + (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer", + me, address_expr); + goto untimely; + } + if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) { + (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing", + me, address_expr); + goto untimely; + } + sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n", + code, (flags & CTL_MORE) != 0 ? '-' : ' ', + text)); + for (pc = sess->outbuf.text, n = 0; + n < (int)sess->outbuf.used-2; pc++, n++) + if (!isascii((unsigned char)*pc) || + !isprint((unsigned char)*pc)) + *pc = '\040'; + *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used); + if (body != NULL) { + char *tmp; + DE_CONST(body, tmp); + *iovp++ = evConsIovec(tmp, bodylen); + DE_CONST(".\r\n", tmp); + *iovp++ = evConsIovec(tmp, 3); + } + (*ctx->logger)(ctl_debug, "%s: [%d] %s", me, + sess->outbuf.used, sess->outbuf.text); + if (evWrite(ctx->ev, sess->sock, iov, iovp - iov, + ctl_writedone, sess, &sess->wrID) < 0) { + (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me, + address_expr, strerror(errno)); + goto untimely; + } + if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout, + &sess->wrtiID) < 0) + { + (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, + address_expr, strerror(errno)); + goto untimely; + } + if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) { + (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me, + address_expr, strerror(errno)); + untimely: + ctl_signal_done(ctx, sess); + ctl_close(sess); + return; + } + sess->respctx = respctx; + sess->respflags = flags; +} + +void +ctl_sendhelp(struct ctl_sess *sess, u_int code) { + static const char me[] = "ctl_sendhelp"; + struct ctl_sctx *ctx = sess->ctx; + + sess->helpcode = code; + sess->verb = &fakehelpverb; + ctl_morehelp(ctx, sess, NULL, me, CTL_MORE, + (const void *)ctx->verbs, NULL); +} + +void * +ctl_getcsctx(struct ctl_sess *sess) { + return (sess->csctx); +} + +void * +ctl_setcsctx(struct ctl_sess *sess, void *csctx) { + void *old = sess->csctx; + + sess->csctx = csctx; + return (old); +} + +/* Private functions. */ + +static void +ctl_accept(evContext lev, void *uap, int fd, + const void *lav, int lalen, + const void *rav, int ralen) +{ + static const char me[] = "ctl_accept"; + struct ctl_sctx *ctx = uap; + struct ctl_sess *sess = NULL; + char tmp[MAX_NTOP]; + + UNUSED(lev); + UNUSED(lalen); + UNUSED(ralen); + + if (fd < 0) { + (*ctx->logger)(ctl_error, "%s: accept: %s", + me, strerror(errno)); + return; + } + if (ctx->cur_sess == ctx->max_sess) { + (*ctx->logger)(ctl_error, "%s: %s: too many control sessions", + me, ctl_sa_ntop((const struct sockaddr *)rav, + tmp, sizeof tmp, + ctx->logger)); + (void) close(fd); + return; + } + sess = memget(sizeof *sess); + if (sess == NULL) { + (*ctx->logger)(ctl_error, "%s: memget: %s", me, + strerror(errno)); + (void) close(fd); + return; + } + if (fcntl(fd, F_SETFD, 1) < 0) { + (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me, + strerror(errno)); + } + ctx->cur_sess++; + INIT_LINK(sess, link); + APPEND(ctx->sess, sess, link); + sess->ctx = ctx; + sess->sock = fd; + sess->wrID.opaque = NULL; + sess->rdID.opaque = NULL; + sess->wrtiID.opaque = NULL; + sess->rdtiID.opaque = NULL; + sess->respctx = NULL; + sess->csctx = NULL; + if (((const struct sockaddr *)rav)->sa_family == AF_UNIX) + ctl_sa_copy((const struct sockaddr *)lav, + (struct sockaddr *)&sess->sa); + else + ctl_sa_copy((const struct sockaddr *)rav, + (struct sockaddr *)&sess->sa); + sess->donefunc = NULL; + buffer_init(sess->inbuf); + buffer_init(sess->outbuf); + sess->state = available; + ctl_new_state(sess, initializing, me); + sess->verb = ctx->connverb; + (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)", + me, address_expr, sess->sock); + (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0, + (const struct sockaddr *)rav, ctx->uctx); +} + +static void +ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason) +{ + static const char me[] = "ctl_new_state"; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP]; + + (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)", + me, address_expr, + state_names[sess->state], + state_names[new_state], reason); + sess->state = new_state; +} + +static void +ctl_close(struct ctl_sess *sess) { + static const char me[] = "ctl_close"; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP]; + + REQUIRE(sess->state == initializing || + sess->state == writing || + sess->state == reading || + sess->state == processing || + sess->state == reading_data || + sess->state == idling); + REQUIRE(sess->sock != -1); + if (sess->state == reading || sess->state == reading_data) + ctl_stop_read(sess); + else if (sess->state == writing) { + if (sess->wrID.opaque != NULL) { + (void) evCancelRW(ctx->ev, sess->wrID); + sess->wrID.opaque = NULL; + } + if (sess->wrtiID.opaque != NULL) { + (void) evClearIdleTimer(ctx->ev, sess->wrtiID); + sess->wrtiID.opaque = NULL; + } + } + ctl_new_state(sess, closing, me); + (void) close(sess->sock); + if (allocated_p(sess->inbuf)) + ctl_bufput(&sess->inbuf); + if (allocated_p(sess->outbuf)) + ctl_bufput(&sess->outbuf); + (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)", + me, address_expr, sess->sock); + UNLINK(ctx->sess, sess, link); + memput(sess, sizeof *sess); + ctx->cur_sess--; +} + +static void +ctl_start_read(struct ctl_sess *sess) { + static const char me[] = "ctl_start_read"; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP]; + + REQUIRE(sess->state == initializing || + sess->state == writing || + sess->state == processing || + sess->state == idling); + REQUIRE(sess->rdtiID.opaque == NULL); + REQUIRE(sess->rdID.opaque == NULL); + sess->inbuf.used = 0; + if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout, + &sess->rdtiID) < 0) + { + (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me, + address_expr, strerror(errno)); + ctl_close(sess); + return; + } + if (evSelectFD(ctx->ev, sess->sock, EV_READ, + ctl_readable, sess, &sess->rdID) < 0) { + (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me, + address_expr, strerror(errno)); + return; + } + ctl_new_state(sess, reading, me); +} + +static void +ctl_stop_read(struct ctl_sess *sess) { + static const char me[] = "ctl_stop_read"; + struct ctl_sctx *ctx = sess->ctx; + + REQUIRE(sess->state == reading || sess->state == reading_data); + REQUIRE(sess->rdID.opaque != NULL); + (void) evDeselectFD(ctx->ev, sess->rdID); + sess->rdID.opaque = NULL; + if (sess->rdtiID.opaque != NULL) { + (void) evClearIdleTimer(ctx->ev, sess->rdtiID); + sess->rdtiID.opaque = NULL; + } + ctl_new_state(sess, idling, me); +} + +static void +ctl_readable(evContext lev, void *uap, int fd, int evmask) { + static const char me[] = "ctl_readable"; + struct ctl_sess *sess = uap; + struct ctl_sctx *ctx; + char *eos, tmp[MAX_NTOP]; + ssize_t n; + + REQUIRE(sess != NULL); + REQUIRE(fd >= 0); + REQUIRE(evmask == EV_READ); + REQUIRE(sess->state == reading || sess->state == reading_data); + + ctx = sess->ctx; + evTouchIdleTimer(lev, sess->rdtiID); + if (!allocated_p(sess->inbuf) && + ctl_bufget(&sess->inbuf, ctx->logger) < 0) { + (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer", + me, address_expr); + ctl_close(sess); + return; + } + n = read(sess->sock, sess->inbuf.text + sess->inbuf.used, + MAX_LINELEN - sess->inbuf.used); + if (n <= 0) { + (*ctx->logger)(ctl_debug, "%s: %s: read: %s", + me, address_expr, + (n == 0) ? "Unexpected EOF" : strerror(errno)); + ctl_close(sess); + return; + } + sess->inbuf.used += n; + eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used); + if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') { + eos[-1] = '\0'; + if ((sess->respflags & CTL_DATA) != 0) { + INSIST(sess->verb != NULL); + (*sess->verb->func)(sess->ctx, sess, sess->verb, + sess->inbuf.text, + CTL_DATA, sess->respctx, + sess->ctx->uctx); + } else { + ctl_stop_read(sess); + ctl_docommand(sess); + } + sess->inbuf.used -= ((eos - sess->inbuf.text) + 1); + if (sess->inbuf.used == 0U) + ctl_bufput(&sess->inbuf); + else + memmove(sess->inbuf.text, eos + 1, sess->inbuf.used); + return; + } + if (sess->inbuf.used == (size_t)MAX_LINELEN) { + (*ctx->logger)(ctl_error, "%s: %s: line too long, closing", + me, address_expr); + ctl_close(sess); + } +} + +static void +ctl_wrtimeout(evContext lev, void *uap, + struct timespec due, + struct timespec itv) +{ + static const char me[] = "ctl_wrtimeout"; + struct ctl_sess *sess = uap; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP]; + + UNUSED(lev); + UNUSED(due); + UNUSED(itv); + + REQUIRE(sess->state == writing); + sess->wrtiID.opaque = NULL; + (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing", + me, address_expr); + if (sess->wrID.opaque != NULL) { + (void) evCancelRW(ctx->ev, sess->wrID); + sess->wrID.opaque = NULL; + } + ctl_signal_done(ctx, sess); + ctl_new_state(sess, processing, me); + ctl_close(sess); +} + +static void +ctl_rdtimeout(evContext lev, void *uap, + struct timespec due, + struct timespec itv) +{ + static const char me[] = "ctl_rdtimeout"; + struct ctl_sess *sess = uap; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP]; + + UNUSED(lev); + UNUSED(due); + UNUSED(itv); + + REQUIRE(sess->state == reading); + sess->rdtiID.opaque = NULL; + (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing", + me, address_expr); + if (sess->state == reading || sess->state == reading_data) + ctl_stop_read(sess); + ctl_signal_done(ctx, sess); + ctl_new_state(sess, processing, me); + ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL, + NULL, NULL, NULL, 0); +} + +static void +ctl_docommand(struct ctl_sess *sess) { + static const char me[] = "ctl_docommand"; + char *name, *rest, tmp[MAX_NTOP]; + struct ctl_sctx *ctx = sess->ctx; + const struct ctl_verb *verb; + + REQUIRE(allocated_p(sess->inbuf)); + (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]", + me, address_expr, + sess->inbuf.text, (u_int)sess->inbuf.used); + ctl_new_state(sess, processing, me); + name = sess->inbuf.text + strspn(sess->inbuf.text, space); + rest = name + strcspn(name, space); + if (*rest != '\0') { + *rest++ = '\0'; + rest += strspn(rest, space); + } + for (verb = ctx->verbs; + verb != NULL && verb->name != NULL && verb->func != NULL; + verb++) + if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0) + break; + if (verb != NULL && verb->name != NULL && verb->func != NULL) { + sess->verb = verb; + (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx); + } else { + char buf[1100]; + + if (sizeof "Unrecognized command \"\" (args \"\")" + + strlen(name) + strlen(rest) > sizeof buf) + strcpy(buf, "Unrecognized command (buf ovf)"); + else + sprintf(buf, + "Unrecognized command \"%s\" (args \"%s\")", + name, rest); + ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL, + NULL, 0); + } +} + +static void +ctl_writedone(evContext lev, void *uap, int fd, int bytes) { + static const char me[] = "ctl_writedone"; + struct ctl_sess *sess = uap; + struct ctl_sctx *ctx = sess->ctx; + char tmp[MAX_NTOP]; + int save_errno = errno; + + UNUSED(lev); + UNUSED(uap); + + REQUIRE(sess->state == writing); + REQUIRE(fd == sess->sock); + REQUIRE(sess->wrtiID.opaque != NULL); + sess->wrID.opaque = NULL; + (void) evClearIdleTimer(ctx->ev, sess->wrtiID); + sess->wrtiID.opaque = NULL; + if (bytes < 0) { + (*ctx->logger)(ctl_error, "%s: %s: %s", + me, address_expr, strerror(save_errno)); + ctl_close(sess); + return; + } + + INSIST(allocated_p(sess->outbuf)); + ctl_bufput(&sess->outbuf); + if ((sess->respflags & CTL_EXIT) != 0) { + ctl_signal_done(ctx, sess); + ctl_close(sess); + return; + } else if ((sess->respflags & CTL_MORE) != 0) { + INSIST(sess->verb != NULL); + (*sess->verb->func)(sess->ctx, sess, sess->verb, "", + CTL_MORE, sess->respctx, sess->ctx->uctx); + } else { + ctl_signal_done(ctx, sess); + ctl_start_read(sess); + } +} + +static void +ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess, + const struct ctl_verb *verb, const char *text, + u_int respflags, const void *respctx, void *uctx) +{ + const struct ctl_verb *this = respctx, *next = this + 1; + + UNUSED(ctx); + UNUSED(verb); + UNUSED(text); + UNUSED(uctx); + + REQUIRE(!lastverb_p(this)); + REQUIRE((respflags & CTL_MORE) != 0); + if (lastverb_p(next)) + respflags &= ~CTL_MORE; + ctl_response(sess, sess->helpcode, this->help, respflags, next, + NULL, NULL, NULL, 0); +} + +static void +ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) { + if (sess->donefunc != NULL) { + (*sess->donefunc)(ctx, sess, sess->uap); + sess->donefunc = NULL; + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c b/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c new file mode 100644 index 0000000000..821eb8ebff --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* ev_connects.c - implement asynch connect/accept for the eventlib + * vix 16sep96 [initial] + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/assertions.h> +#include "eventlib_p.h" + +#include "port_after.h" + +/* Macros. */ + +#define GETXXXNAME(f, s, sa, len) ( \ + (f((s), (&sa), (&len)) >= 0) ? 0 : \ + (errno != EAFNOSUPPORT && errno != EOPNOTSUPP) ? -1 : ( \ + memset(&(sa), 0, sizeof (sa)), \ + (len) = sizeof (sa), \ + (sa).sa_family = AF_UNIX, \ + 0 \ + ) \ + ) + +/* Forward. */ + +static void listener(evContext ctx, void *uap, int fd, int evmask); +static void connector(evContext ctx, void *uap, int fd, int evmask); + +/* Public. */ + +int +evListen(evContext opaqueCtx, int fd, int maxconn, + evConnFunc func, void *uap, evConnID *id) +{ + evContext_p *ctx = opaqueCtx.opaque; + evConn *new; + int mode; + + OKNEW(new); + new->flags = EV_CONN_LISTEN; + OKFREE(mode = fcntl(fd, F_GETFL, NULL), new); /*%< side effect: validate fd. */ + /* + * Remember the nonblocking status. We assume that either evSelectFD + * has not been done to this fd, or that if it has then the caller + * will evCancelConn before they evDeselectFD. If our assumptions + * are not met, then we might restore the old nonblocking status + * incorrectly. + */ + if ((mode & PORT_NONBLOCK) == 0) { +#ifdef USE_FIONBIO_IOCTL + int on = 1; + OKFREE(ioctl(fd, FIONBIO, (char *)&on), new); +#else + OKFREE(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK), new); +#endif + new->flags |= EV_CONN_BLOCK; + } + OKFREE(listen(fd, maxconn), new); + if (evSelectFD(opaqueCtx, fd, EV_READ, listener, new, &new->file) < 0){ + int save = errno; + + FREE(new); + errno = save; + return (-1); + } + new->flags |= EV_CONN_SELECTED; + new->func = func; + new->uap = uap; + new->fd = fd; + if (ctx->conns != NULL) + ctx->conns->prev = new; + new->prev = NULL; + new->next = ctx->conns; + ctx->conns = new; + if (id) + id->opaque = new; + return (0); +} + +int +evConnect(evContext opaqueCtx, int fd, const void *ra, int ralen, + evConnFunc func, void *uap, evConnID *id) +{ + evContext_p *ctx = opaqueCtx.opaque; + evConn *new; + + OKNEW(new); + new->flags = 0; + /* Do the select() first to get the socket into nonblocking mode. */ + if (evSelectFD(opaqueCtx, fd, EV_MASK_ALL, + connector, new, &new->file) < 0) { + int save = errno; + + FREE(new); + errno = save; + return (-1); + } + new->flags |= EV_CONN_SELECTED; + if (connect(fd, ra, ralen) < 0 && + errno != EWOULDBLOCK && + errno != EAGAIN && + errno != EINPROGRESS) { + int save = errno; + + (void) evDeselectFD(opaqueCtx, new->file); + FREE(new); + errno = save; + return (-1); + } + /* No error, or EWOULDBLOCK. select() tells when it's ready. */ + new->func = func; + new->uap = uap; + new->fd = fd; + if (ctx->conns != NULL) + ctx->conns->prev = new; + new->prev = NULL; + new->next = ctx->conns; + ctx->conns = new; + if (id) + id->opaque = new; + return (0); +} + +int +evCancelConn(evContext opaqueCtx, evConnID id) { + evContext_p *ctx = opaqueCtx.opaque; + evConn *this = id.opaque; + evAccept *acc, *nxtacc; + int mode; + + if ((this->flags & EV_CONN_SELECTED) != 0) + (void) evDeselectFD(opaqueCtx, this->file); + if ((this->flags & EV_CONN_BLOCK) != 0) { + mode = fcntl(this->fd, F_GETFL, NULL); + if (mode == -1) { + if (errno != EBADF) + return (-1); + } else { +#ifdef USE_FIONBIO_IOCTL + int off = 0; + OK(ioctl(this->fd, FIONBIO, (char *)&off)); +#else + OK(fcntl(this->fd, F_SETFL, mode & ~PORT_NONBLOCK)); +#endif + } + } + + /* Unlink from ctx->conns. */ + if (this->prev != NULL) + this->prev->next = this->next; + else + ctx->conns = this->next; + if (this->next != NULL) + this->next->prev = this->prev; + + /* + * Remove `this' from the ctx->accepts list (zero or more times). + */ + for (acc = HEAD(ctx->accepts), nxtacc = NULL; + acc != NULL; + acc = nxtacc) + { + nxtacc = NEXT(acc, link); + if (acc->conn == this) { + UNLINK(ctx->accepts, acc, link); + close(acc->fd); + FREE(acc); + } + } + + /* Wrap up and get out. */ + FREE(this); + return (0); +} + +int evHold(evContext opaqueCtx, evConnID id) { + evConn *this = id.opaque; + + if ((this->flags & EV_CONN_LISTEN) == 0) { + errno = EINVAL; + return (-1); + } + if ((this->flags & EV_CONN_SELECTED) == 0) + return (0); + this->flags &= ~EV_CONN_SELECTED; + return (evDeselectFD(opaqueCtx, this->file)); +} + +int evUnhold(evContext opaqueCtx, evConnID id) { + evConn *this = id.opaque; + int ret; + + if ((this->flags & EV_CONN_LISTEN) == 0) { + errno = EINVAL; + return (-1); + } + if ((this->flags & EV_CONN_SELECTED) != 0) + return (0); + ret = evSelectFD(opaqueCtx, this->fd, EV_READ, listener, this, + &this->file); + if (ret == 0) + this->flags |= EV_CONN_SELECTED; + return (ret); +} + +int +evTryAccept(evContext opaqueCtx, evConnID id, int *sys_errno) { + evContext_p *ctx = opaqueCtx.opaque; + evConn *conn = id.opaque; + evAccept *new; + + if ((conn->flags & EV_CONN_LISTEN) == 0) { + errno = EINVAL; + return (-1); + } + OKNEW(new); + new->conn = conn; + new->ralen = sizeof new->ra; + new->fd = accept(conn->fd, &new->ra.sa, &new->ralen); + if (new->fd > ctx->highestFD) { + close(new->fd); + new->fd = -1; + new->ioErrno = ENOTSOCK; + } + if (new->fd >= 0) { + new->lalen = sizeof new->la; + if (GETXXXNAME(getsockname, new->fd, new->la.sa, new->lalen) < 0) { + new->ioErrno = errno; + (void) close(new->fd); + new->fd = -1; + } else + new->ioErrno = 0; + } else { + new->ioErrno = errno; + if (errno == EAGAIN || errno == EWOULDBLOCK) { + FREE(new); + return (-1); + } + } + INIT_LINK(new, link); + APPEND(ctx->accepts, new, link); + *sys_errno = new->ioErrno; + return (0); +} + +/* Private. */ + +static void +listener(evContext opaqueCtx, void *uap, int fd, int evmask) { + evContext_p *ctx = opaqueCtx.opaque; + evConn *conn = uap; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } la, ra; + int new; + ISC_SOCKLEN_T lalen = 0, ralen; + + REQUIRE((evmask & EV_READ) != 0); + ralen = sizeof ra; + new = accept(fd, &ra.sa, &ralen); + if (new > ctx->highestFD) { + close(new); + new = -1; + errno = ENOTSOCK; + } + if (new >= 0) { + lalen = sizeof la; + if (GETXXXNAME(getsockname, new, la.sa, lalen) < 0) { + int save = errno; + + (void) close(new); + errno = save; + new = -1; + } + } else if (errno == EAGAIN || errno == EWOULDBLOCK) + return; + (*conn->func)(opaqueCtx, conn->uap, new, &la.sa, lalen, &ra.sa, ralen); +} + +static void +connector(evContext opaqueCtx, void *uap, int fd, int evmask) { + evConn *conn = uap; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } la, ra; + ISC_SOCKLEN_T lalen, ralen; +#ifndef NETREAD_BROKEN + char buf[1]; +#endif + void *conn_uap; + evConnFunc conn_func; + evConnID id; + int socket_errno = 0; + ISC_SOCKLEN_T optlen; + + UNUSED(evmask); + + lalen = sizeof la; + ralen = sizeof ra; + conn_uap = conn->uap; + conn_func = conn->func; + id.opaque = conn; +#ifdef SO_ERROR + optlen = sizeof socket_errno; + if (fd < 0 && + getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (char *)&socket_errno, + &optlen) < 0) + socket_errno = errno; + else + errno = socket_errno; +#endif + if (evCancelConn(opaqueCtx, id) < 0 || + socket_errno || +#ifdef NETREAD_BROKEN + 0 || +#else + read(fd, buf, 0) < 0 || +#endif + GETXXXNAME(getsockname, fd, la.sa, lalen) < 0 || + GETXXXNAME(getpeername, fd, ra.sa, ralen) < 0) { + int save = errno; + + (void) close(fd); /*%< XXX closing caller's fd */ + errno = save; + fd = -1; + } + (*conn_func)(opaqueCtx, conn_uap, fd, &la.sa, lalen, &ra.sa, ralen); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_files.c b/usr/src/lib/libresolv2_joy/common/isc/ev_files.c new file mode 100644 index 0000000000..f75ff6b646 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_files.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* ev_files.c - implement asynch file IO for the eventlib + * vix 11sep95 [initial] + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include "eventlib_p.h" + +#include "port_after.h" + +static evFile *FindFD(const evContext_p *ctx, int fd, int eventmask); + +int +evSelectFD(evContext opaqueCtx, + int fd, + int eventmask, + evFileFunc func, + void *uap, + evFileID *opaqueID +) { + evContext_p *ctx = opaqueCtx.opaque; + evFile *id; + int mode; + + evPrintf(ctx, 1, + "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n", + ctx, fd, eventmask, func, uap); + if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0) + EV_ERR(EINVAL); +#ifndef USE_POLL + if (fd > ctx->highestFD) + EV_ERR(EINVAL); +#endif + OK(mode = fcntl(fd, F_GETFL, NULL)); /*%< side effect: validate fd. */ + /* + * The first time we touch a file descriptor, we need to check to see + * if the application already had it in O_NONBLOCK mode and if so, all + * of our deselect()'s have to leave it in O_NONBLOCK. If not, then + * all but our last deselect() has to leave it in O_NONBLOCK. + */ +#ifdef USE_POLL + /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */ + if (fd >= ctx->maxnfds && evPollfdRealloc(ctx, 1, fd) != 0) + EV_ERR(ENOMEM); +#endif /* USE_POLL */ + id = FindFD(ctx, fd, EV_MASK_ALL); + if (id == NULL) { + if (mode & PORT_NONBLOCK) + FD_SET(fd, &ctx->nonblockBefore); + else { +#ifdef USE_FIONBIO_IOCTL + int on = 1; + OK(ioctl(fd, FIONBIO, (char *)&on)); +#else + OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK)); +#endif + FD_CLR(fd, &ctx->nonblockBefore); + } + } + + /* + * If this descriptor is already in use, search for it again to see + * if any of the eventmask bits we want to set are already captured. + * We cannot usefully capture the same fd event more than once in the + * same context. + */ + if (id != NULL && FindFD(ctx, fd, eventmask) != NULL) + EV_ERR(ETOOMANYREFS); + + /* Allocate and fill. */ + OKNEW(id); + id->func = func; + id->uap = uap; + id->fd = fd; + id->eventmask = eventmask; + + /* + * Insert at head. Order could be important for performance if we + * believe that evGetNext()'s accesses to the fd_sets will be more + * serial and therefore more cache-lucky if the list is ordered by + * ``fd.'' We do not believe these things, so we don't do it. + * + * The interesting sequence is where GetNext() has cached a select() + * result and the caller decides to evSelectFD() on some descriptor. + * Since GetNext() starts at the head, it can miss new entries we add + * at the head. This is not a serious problem since the event being + * evSelectFD()'d for has to occur before evSelectFD() is called for + * the file event to be considered "missed" -- a real corner case. + * Maintaining a "tail" pointer for ctx->files would fix this, but I'm + * not sure it would be ``more correct.'' + */ + if (ctx->files != NULL) + ctx->files->prev = id; + id->prev = NULL; + id->next = ctx->files; + ctx->files = id; + + /* Insert into fd table. */ + if (ctx->fdTable[fd] != NULL) + ctx->fdTable[fd]->fdprev = id; + id->fdprev = NULL; + id->fdnext = ctx->fdTable[fd]; + ctx->fdTable[fd] = id; + + /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */ + if (eventmask & EV_READ) + FD_SET(fd, &ctx->rdNext); + if (eventmask & EV_WRITE) + FD_SET(fd, &ctx->wrNext); + if (eventmask & EV_EXCEPT) + FD_SET(fd, &ctx->exNext); + + /* Update fdMax. */ + if (fd > ctx->fdMax) + ctx->fdMax = fd; + + /* Remember the ID if the caller provided us a place for it. */ + if (opaqueID) + opaqueID->opaque = id; + + return (0); +} + +int +evDeselectFD(evContext opaqueCtx, evFileID opaqueID) { + evContext_p *ctx = opaqueCtx.opaque; + evFile *del = opaqueID.opaque; + evFile *cur; + int mode, eventmask; + + if (!del) { + evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n"); + errno = EINVAL; + return (-1); + } + + evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n", + del->fd, del->eventmask); + + /* Get the mode. Unless the file has been closed, errors are bad. */ + mode = fcntl(del->fd, F_GETFL, NULL); + if (mode == -1 && errno != EBADF) + EV_ERR(errno); + + /* Remove from the list of files. */ + if (del->prev != NULL) + del->prev->next = del->next; + else + ctx->files = del->next; + if (del->next != NULL) + del->next->prev = del->prev; + + /* Remove from the fd table. */ + if (del->fdprev != NULL) + del->fdprev->fdnext = del->fdnext; + else + ctx->fdTable[del->fd] = del->fdnext; + if (del->fdnext != NULL) + del->fdnext->fdprev = del->fdprev; + + /* + * If the file descriptor does not appear in any other select() entry, + * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode + * earlier, then: restore the fd to blocking status. + */ + if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) && + !FD_ISSET(del->fd, &ctx->nonblockBefore) && + mode != -1) { + /* + * Note that we won't return an error status to the caller if + * this fcntl() fails since (a) we've already done the work + * and (b) the caller didn't ask us anything about O_NONBLOCK. + */ +#ifdef USE_FIONBIO_IOCTL + int off = 0; + (void) ioctl(del->fd, FIONBIO, (char *)&off); +#else + (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK); +#endif + } + + /* + * Now find all other uses of this descriptor and OR together an event + * mask so that we don't turn off {rd,wr,ex}Next bits that some other + * file event is using. As an optimization, stop if the event mask + * fills. + */ + eventmask = 0; + for ((void)NULL; + cur != NULL && eventmask != EV_MASK_ALL; + cur = cur->next) + if (cur->fd == del->fd) + eventmask |= cur->eventmask; + + /* OK, now we know which bits we can clear out. */ + if (!(eventmask & EV_READ)) { + FD_CLR(del->fd, &ctx->rdNext); + if (FD_ISSET(del->fd, &ctx->rdLast)) { + FD_CLR(del->fd, &ctx->rdLast); + ctx->fdCount--; + } + } + if (!(eventmask & EV_WRITE)) { + FD_CLR(del->fd, &ctx->wrNext); + if (FD_ISSET(del->fd, &ctx->wrLast)) { + FD_CLR(del->fd, &ctx->wrLast); + ctx->fdCount--; + } + } + if (!(eventmask & EV_EXCEPT)) { + FD_CLR(del->fd, &ctx->exNext); + if (FD_ISSET(del->fd, &ctx->exLast)) { + FD_CLR(del->fd, &ctx->exLast); + ctx->fdCount--; + } + } + + /* If this was the maxFD, find the new one. */ + if (del->fd == ctx->fdMax) { + ctx->fdMax = -1; + for (cur = ctx->files; cur; cur = cur->next) + if (cur->fd > ctx->fdMax) + ctx->fdMax = cur->fd; + } + + /* If this was the fdNext, cycle that to the next entry. */ + if (del == ctx->fdNext) + ctx->fdNext = del->next; + + /* Couldn't free it before now since we were using fields out of it. */ + FREE(del); + + return (0); +} + +static evFile * +FindFD(const evContext_p *ctx, int fd, int eventmask) { + evFile *id; + + for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext) + if (id->fd == fd && (id->eventmask & eventmask) != 0) + break; + return (id); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c b/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c new file mode 100644 index 0000000000..824b4e25c1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* ev_streams.c - implement asynch stream file IO for the eventlib + * vix 04mar96 [initial] + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/uio.h> + +#include <errno.h> + +#include <isc/eventlib.h> +#include <isc/assertions.h> +#include "eventlib_p.h" + +#include "port_after.h" + +static int copyvec(evStream *str, const struct iovec *iov, int iocnt); +static void consume(evStream *str, size_t bytes); +static void done(evContext opaqueCtx, evStream *str); +static void writable(evContext opaqueCtx, void *uap, int fd, int evmask); +static void readable(evContext opaqueCtx, void *uap, int fd, int evmask); + +struct iovec +evConsIovec(void *buf, size_t cnt) { + struct iovec ret; + + memset(&ret, 0xf5, sizeof ret); + ret.iov_base = buf; + ret.iov_len = cnt; + return (ret); +} + +int +evWrite(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt, + evStreamFunc func, void *uap, evStreamID *id) +{ + evContext_p *ctx = opaqueCtx.opaque; + evStream *new; + int save; + + OKNEW(new); + new->func = func; + new->uap = uap; + new->fd = fd; + new->flags = 0; + if (evSelectFD(opaqueCtx, fd, EV_WRITE, writable, new, &new->file) < 0) + goto free; + if (copyvec(new, iov, iocnt) < 0) + goto free; + new->prevDone = NULL; + new->nextDone = NULL; + if (ctx->streams != NULL) + ctx->streams->prev = new; + new->prev = NULL; + new->next = ctx->streams; + ctx->streams = new; + if (id != NULL) + id->opaque = new; + return (0); + free: + save = errno; + FREE(new); + errno = save; + return (-1); +} + +int +evRead(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt, + evStreamFunc func, void *uap, evStreamID *id) +{ + evContext_p *ctx = opaqueCtx.opaque; + evStream *new; + int save; + + OKNEW(new); + new->func = func; + new->uap = uap; + new->fd = fd; + new->flags = 0; + if (evSelectFD(opaqueCtx, fd, EV_READ, readable, new, &new->file) < 0) + goto free; + if (copyvec(new, iov, iocnt) < 0) + goto free; + new->prevDone = NULL; + new->nextDone = NULL; + if (ctx->streams != NULL) + ctx->streams->prev = new; + new->prev = NULL; + new->next = ctx->streams; + ctx->streams = new; + if (id) + id->opaque = new; + return (0); + free: + save = errno; + FREE(new); + errno = save; + return (-1); +} + +int +evTimeRW(evContext opaqueCtx, evStreamID id, evTimerID timer) /*ARGSUSED*/ { + evStream *str = id.opaque; + + UNUSED(opaqueCtx); + + str->timer = timer; + str->flags |= EV_STR_TIMEROK; + return (0); +} + +int +evUntimeRW(evContext opaqueCtx, evStreamID id) /*ARGSUSED*/ { + evStream *str = id.opaque; + + UNUSED(opaqueCtx); + + str->flags &= ~EV_STR_TIMEROK; + return (0); +} + +int +evCancelRW(evContext opaqueCtx, evStreamID id) { + evContext_p *ctx = opaqueCtx.opaque; + evStream *old = id.opaque; + + /* + * The streams list is doubly threaded. First, there's ctx->streams + * that's used by evDestroy() to find and cancel all streams. Second, + * there's ctx->strDone (head) and ctx->strLast (tail) which thread + * through the potentially smaller number of "IO completed" streams, + * used in evGetNext() to avoid scanning the entire list. + */ + + /* Unlink from ctx->streams. */ + if (old->prev != NULL) + old->prev->next = old->next; + else + ctx->streams = old->next; + if (old->next != NULL) + old->next->prev = old->prev; + + /* + * If 'old' is on the ctx->strDone list, remove it. Update + * ctx->strLast if necessary. + */ + if (old->prevDone == NULL && old->nextDone == NULL) { + /* + * Either 'old' is the only item on the done list, or it's + * not on the done list. If the former, then we unlink it + * from the list. If the latter, we leave the list alone. + */ + if (ctx->strDone == old) { + ctx->strDone = NULL; + ctx->strLast = NULL; + } + } else { + if (old->prevDone != NULL) + old->prevDone->nextDone = old->nextDone; + else + ctx->strDone = old->nextDone; + if (old->nextDone != NULL) + old->nextDone->prevDone = old->prevDone; + else + ctx->strLast = old->prevDone; + } + + /* Deallocate the stream. */ + if (old->file.opaque) + evDeselectFD(opaqueCtx, old->file); + memput(old->iovOrig, sizeof (struct iovec) * old->iovOrigCount); + FREE(old); + return (0); +} + +/* Copy a scatter/gather vector and initialize a stream handler's IO. */ +static int +copyvec(evStream *str, const struct iovec *iov, int iocnt) { + int i; + + str->iovOrig = (struct iovec *)memget(sizeof(struct iovec) * iocnt); + if (str->iovOrig == NULL) { + errno = ENOMEM; + return (-1); + } + str->ioTotal = 0; + for (i = 0; i < iocnt; i++) { + str->iovOrig[i] = iov[i]; + str->ioTotal += iov[i].iov_len; + } + str->iovOrigCount = iocnt; + str->iovCur = str->iovOrig; + str->iovCurCount = str->iovOrigCount; + str->ioDone = 0; + return (0); +} + +/* Pull off or truncate lead iovec(s). */ +static void +consume(evStream *str, size_t bytes) { + while (bytes > 0U) { + if (bytes < (size_t)str->iovCur->iov_len) { + str->iovCur->iov_len -= bytes; + str->iovCur->iov_base = (void *) + ((u_char *)str->iovCur->iov_base + bytes); + str->ioDone += bytes; + bytes = 0; + } else { + bytes -= str->iovCur->iov_len; + str->ioDone += str->iovCur->iov_len; + str->iovCur++; + str->iovCurCount--; + } + } +} + +/* Add a stream to Done list and deselect the FD. */ +static void +done(evContext opaqueCtx, evStream *str) { + evContext_p *ctx = opaqueCtx.opaque; + + if (ctx->strLast != NULL) { + str->prevDone = ctx->strLast; + ctx->strLast->nextDone = str; + ctx->strLast = str; + } else { + INSIST(ctx->strDone == NULL); + ctx->strDone = ctx->strLast = str; + } + evDeselectFD(opaqueCtx, str->file); + str->file.opaque = NULL; + /* evDrop() will call evCancelRW() on us. */ +} + +/* Dribble out some bytes on the stream. (Called by evDispatch().) */ +static void +writable(evContext opaqueCtx, void *uap, int fd, int evmask) { + evStream *str = uap; + int bytes; + + UNUSED(evmask); + + bytes = writev(fd, str->iovCur, str->iovCurCount); + if (bytes > 0) { + if ((str->flags & EV_STR_TIMEROK) != 0) + evTouchIdleTimer(opaqueCtx, str->timer); + consume(str, bytes); + } else { + if (bytes < 0 && errno != EINTR) { + str->ioDone = -1; + str->ioErrno = errno; + } + } + if (str->ioDone == -1 || str->ioDone == str->ioTotal) + done(opaqueCtx, str); +} + +/* Scoop up some bytes from the stream. (Called by evDispatch().) */ +static void +readable(evContext opaqueCtx, void *uap, int fd, int evmask) { + evStream *str = uap; + int bytes; + + UNUSED(evmask); + + bytes = readv(fd, str->iovCur, str->iovCurCount); + if (bytes > 0) { + if ((str->flags & EV_STR_TIMEROK) != 0) + evTouchIdleTimer(opaqueCtx, str->timer); + consume(str, bytes); + } else { + if (bytes == 0) + str->ioDone = 0; + else { + if (errno != EINTR) { + str->ioDone = -1; + str->ioErrno = errno; + } + } + } + if (str->ioDone <= 0 || str->ioDone == str->ioTotal) + done(opaqueCtx, str); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c b/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c new file mode 100644 index 0000000000..4aab57f5ff --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* ev_timers.c - implement timers for the eventlib + * vix 09sep95 [initial] + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <errno.h> + +#include <isc/assertions.h> +#include <isc/eventlib.h> +#include "eventlib_p.h" + +#include "port_after.h" + +/* Constants. */ + +#define MILLION 1000000 +#define BILLION 1000000000 + +/* Forward. */ + +static int due_sooner(void *, void *); +static void set_index(void *, int); +static void free_timer(void *, void *); +static void print_timer(void *, void *); +static void idle_timeout(evContext, void *, struct timespec, struct timespec); + +/* Private type. */ + +typedef struct { + evTimerFunc func; + void * uap; + struct timespec lastTouched; + struct timespec max_idle; + evTimer * timer; +} idle_timer; + +/* Public. */ + +struct timespec +evConsTime(time_t sec, long nsec) { + struct timespec x; + + x.tv_sec = sec; + x.tv_nsec = nsec; + return (x); +} + +struct timespec +evAddTime(struct timespec addend1, struct timespec addend2) { + struct timespec x; + + x.tv_sec = addend1.tv_sec + addend2.tv_sec; + x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec; + if (x.tv_nsec >= BILLION) { + x.tv_sec++; + x.tv_nsec -= BILLION; + } + return (x); +} + +struct timespec +evSubTime(struct timespec minuend, struct timespec subtrahend) { + struct timespec x; + + x.tv_sec = minuend.tv_sec - subtrahend.tv_sec; + if (minuend.tv_nsec >= subtrahend.tv_nsec) + x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec; + else { + x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec; + x.tv_sec--; + } + return (x); +} + +int +evCmpTime(struct timespec a, struct timespec b) { + long x = a.tv_sec - b.tv_sec; + + if (x == 0L) + x = a.tv_nsec - b.tv_nsec; + return (x < 0L ? (-1) : x > 0L ? (1) : (0)); +} + +struct timespec +evNowTime() { + struct timeval now; +#ifdef CLOCK_REALTIME + struct timespec tsnow; + int m = CLOCK_REALTIME; + +#ifdef CLOCK_MONOTONIC + if (__evOptMonoTime) + m = CLOCK_MONOTONIC; +#endif + if (clock_gettime(m, &tsnow) == 0) + return (tsnow); +#endif + if (gettimeofday(&now, NULL) < 0) + return (evConsTime(0, 0)); + return (evTimeSpec(now)); +} + +struct timespec +evUTCTime() { + struct timeval now; +#ifdef CLOCK_REALTIME + struct timespec tsnow; + if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) + return (tsnow); +#endif + if (gettimeofday(&now, NULL) < 0) + return (evConsTime(0, 0)); + return (evTimeSpec(now)); +} + +struct timespec +evLastEventTime(evContext opaqueCtx) { + evContext_p *ctx = opaqueCtx.opaque; + + return (ctx->lastEventTime); +} + +struct timespec +evTimeSpec(struct timeval tv) { + struct timespec ts; + + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + return (ts); +} + +struct timeval +evTimeVal(struct timespec ts) { + struct timeval tv; + + tv.tv_sec = ts.tv_sec; + tv.tv_usec = ts.tv_nsec / 1000; + return (tv); +} + +int +evSetTimer(evContext opaqueCtx, + evTimerFunc func, + void *uap, + struct timespec due, + struct timespec inter, + evTimerID *opaqueID +) { + evContext_p *ctx = opaqueCtx.opaque; + evTimer *id; + + evPrintf(ctx, 1, +"evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n", + ctx, func, uap, + (long)due.tv_sec, due.tv_nsec, + (long)inter.tv_sec, inter.tv_nsec); + +#ifdef __hpux + /* + * tv_sec and tv_nsec are unsigned. + */ + if (due.tv_nsec >= BILLION) + EV_ERR(EINVAL); + + if (inter.tv_nsec >= BILLION) + EV_ERR(EINVAL); +#else + if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION) + EV_ERR(EINVAL); + + if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION) + EV_ERR(EINVAL); +#endif + + /* due={0,0} is a magic cookie meaning "now." */ + if (due.tv_sec == (time_t)0 && due.tv_nsec == 0L) + due = evNowTime(); + + /* Allocate and fill. */ + OKNEW(id); + id->func = func; + id->uap = uap; + id->due = due; + id->inter = inter; + + if (heap_insert(ctx->timers, id) < 0) + return (-1); + + /* Remember the ID if the caller provided us a place for it. */ + if (opaqueID) + opaqueID->opaque = id; + + if (ctx->debug > 7) { + evPrintf(ctx, 7, "timers after evSetTimer:\n"); + (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); + } + + return (0); +} + +int +evClearTimer(evContext opaqueCtx, evTimerID id) { + evContext_p *ctx = opaqueCtx.opaque; + evTimer *del = id.opaque; + + if (ctx->cur != NULL && + ctx->cur->type == Timer && + ctx->cur->u.timer.this == del) { + evPrintf(ctx, 8, "deferring delete of timer (executing)\n"); + /* + * Setting the interval to zero ensures that evDrop() will + * clean up the timer. + */ + del->inter = evConsTime(0, 0); + return (0); + } + + if (heap_element(ctx->timers, del->index) != del) + EV_ERR(ENOENT); + + if (heap_delete(ctx->timers, del->index) < 0) + return (-1); + FREE(del); + + if (ctx->debug > 7) { + evPrintf(ctx, 7, "timers after evClearTimer:\n"); + (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); + } + + return (0); +} + +int +evConfigTimer(evContext opaqueCtx, + evTimerID id, + const char *param, + int value +) { + evContext_p *ctx = opaqueCtx.opaque; + evTimer *timer = id.opaque; + int result=0; + + UNUSED(value); + + if (heap_element(ctx->timers, timer->index) != timer) + EV_ERR(ENOENT); + + if (strcmp(param, "rate") == 0) + timer->mode |= EV_TMR_RATE; + else if (strcmp(param, "interval") == 0) + timer->mode &= ~EV_TMR_RATE; + else + EV_ERR(EINVAL); + + return (result); +} + +int +evResetTimer(evContext opaqueCtx, + evTimerID id, + evTimerFunc func, + void *uap, + struct timespec due, + struct timespec inter +) { + evContext_p *ctx = opaqueCtx.opaque; + evTimer *timer = id.opaque; + struct timespec old_due; + int result=0; + + if (heap_element(ctx->timers, timer->index) != timer) + EV_ERR(ENOENT); + +#ifdef __hpux + /* + * tv_sec and tv_nsec are unsigned. + */ + if (due.tv_nsec >= BILLION) + EV_ERR(EINVAL); + + if (inter.tv_nsec >= BILLION) + EV_ERR(EINVAL); +#else + if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION) + EV_ERR(EINVAL); + + if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION) + EV_ERR(EINVAL); +#endif + + old_due = timer->due; + + timer->func = func; + timer->uap = uap; + timer->due = due; + timer->inter = inter; + + switch (evCmpTime(due, old_due)) { + case -1: + result = heap_increased(ctx->timers, timer->index); + break; + case 0: + result = 0; + break; + case 1: + result = heap_decreased(ctx->timers, timer->index); + break; + } + + if (ctx->debug > 7) { + evPrintf(ctx, 7, "timers after evResetTimer:\n"); + (void) heap_for_each(ctx->timers, print_timer, (void *)ctx); + } + + return (result); +} + +int +evSetIdleTimer(evContext opaqueCtx, + evTimerFunc func, + void *uap, + struct timespec max_idle, + evTimerID *opaqueID +) { + evContext_p *ctx = opaqueCtx.opaque; + idle_timer *tt; + + /* Allocate and fill. */ + OKNEW(tt); + tt->func = func; + tt->uap = uap; + tt->lastTouched = ctx->lastEventTime; + tt->max_idle = max_idle; + + if (evSetTimer(opaqueCtx, idle_timeout, tt, + evAddTime(ctx->lastEventTime, max_idle), + max_idle, opaqueID) < 0) { + FREE(tt); + return (-1); + } + + tt->timer = opaqueID->opaque; + + return (0); +} + +int +evClearIdleTimer(evContext opaqueCtx, evTimerID id) { + evTimer *del = id.opaque; + idle_timer *tt = del->uap; + + FREE(tt); + return (evClearTimer(opaqueCtx, id)); +} + +int +evResetIdleTimer(evContext opaqueCtx, + evTimerID opaqueID, + evTimerFunc func, + void *uap, + struct timespec max_idle +) { + evContext_p *ctx = opaqueCtx.opaque; + evTimer *timer = opaqueID.opaque; + idle_timer *tt = timer->uap; + + tt->func = func; + tt->uap = uap; + tt->lastTouched = ctx->lastEventTime; + tt->max_idle = max_idle; + + return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt, + evAddTime(ctx->lastEventTime, max_idle), + max_idle)); +} + +int +evTouchIdleTimer(evContext opaqueCtx, evTimerID id) { + evContext_p *ctx = opaqueCtx.opaque; + evTimer *t = id.opaque; + idle_timer *tt = t->uap; + + tt->lastTouched = ctx->lastEventTime; + + return (0); +} + +/* Public to the rest of eventlib. */ + +heap_context +evCreateTimers(const evContext_p *ctx) { + + UNUSED(ctx); + + return (heap_new(due_sooner, set_index, 2048)); +} + +void +evDestroyTimers(const evContext_p *ctx) { + (void) heap_for_each(ctx->timers, free_timer, NULL); + (void) heap_free(ctx->timers); +} + +/* Private. */ + +static int +due_sooner(void *a, void *b) { + evTimer *a_timer, *b_timer; + + a_timer = a; + b_timer = b; + return (evCmpTime(a_timer->due, b_timer->due) < 0); +} + +static void +set_index(void *what, int index) { + evTimer *timer; + + timer = what; + timer->index = index; +} + +static void +free_timer(void *what, void *uap) { + evTimer *t = what; + + UNUSED(uap); + + FREE(t); +} + +static void +print_timer(void *what, void *uap) { + evTimer *cur = what; + evContext_p *ctx = uap; + + cur = what; + evPrintf(ctx, 7, + " func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n", + cur->func, cur->uap, + (long)cur->due.tv_sec, cur->due.tv_nsec, + (long)cur->inter.tv_sec, cur->inter.tv_nsec); +} + +static void +idle_timeout(evContext opaqueCtx, + void *uap, + struct timespec due, + struct timespec inter +) { + evContext_p *ctx = opaqueCtx.opaque; + idle_timer *this = uap; + struct timespec idle; + + UNUSED(due); + UNUSED(inter); + + idle = evSubTime(ctx->lastEventTime, this->lastTouched); + if (evCmpTime(idle, this->max_idle) >= 0) { + (this->func)(opaqueCtx, this->uap, this->timer->due, + this->max_idle); + /* + * Setting the interval to zero will cause the timer to + * be cleaned up in evDrop(). + */ + this->timer->inter = evConsTime(0, 0); + FREE(this); + } else { + /* evDrop() will reschedule the timer. */ + this->timer->inter = evSubTime(this->max_idle, idle); + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c b/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c new file mode 100644 index 0000000000..9c4e819eb8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* ev_waits.c - implement deferred function calls for the eventlib + * vix 05dec95 [initial] + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <errno.h> + +#include <isc/eventlib.h> +#include <isc/assertions.h> +#include "eventlib_p.h" + +#include "port_after.h" + +/* Forward. */ + +static void print_waits(evContext_p *ctx); +static evWaitList * evNewWaitList(evContext_p *); +static void evFreeWaitList(evContext_p *, evWaitList *); +static evWaitList * evGetWaitList(evContext_p *, const void *, int); + + +/* Public. */ + +/*% + * Enter a new wait function on the queue. + */ +int +evWaitFor(evContext opaqueCtx, const void *tag, + evWaitFunc func, void *uap, evWaitID *id) +{ + evContext_p *ctx = opaqueCtx.opaque; + evWait *new; + evWaitList *wl = evGetWaitList(ctx, tag, 1); + + OKNEW(new); + new->func = func; + new->uap = uap; + new->tag = tag; + new->next = NULL; + if (wl->last != NULL) + wl->last->next = new; + else + wl->first = new; + wl->last = new; + if (id != NULL) + id->opaque = new; + if (ctx->debug >= 9) + print_waits(ctx); + return (0); +} + +/*% + * Mark runnable all waiting functions having a certain tag. + */ +int +evDo(evContext opaqueCtx, const void *tag) { + evContext_p *ctx = opaqueCtx.opaque; + evWaitList *wl = evGetWaitList(ctx, tag, 0); + evWait *first; + + if (!wl) { + errno = ENOENT; + return (-1); + } + + first = wl->first; + INSIST(first != NULL); + + if (ctx->waitDone.last != NULL) + ctx->waitDone.last->next = first; + else + ctx->waitDone.first = first; + ctx->waitDone.last = wl->last; + evFreeWaitList(ctx, wl); + + return (0); +} + +/*% + * Remove a waiting (or ready to run) function from the queue. + */ +int +evUnwait(evContext opaqueCtx, evWaitID id) { + evContext_p *ctx = opaqueCtx.opaque; + evWait *this, *prev; + evWaitList *wl; + int found = 0; + + this = id.opaque; + INSIST(this != NULL); + wl = evGetWaitList(ctx, this->tag, 0); + if (wl != NULL) { + for (prev = NULL, this = wl->first; + this != NULL; + prev = this, this = this->next) + if (this == (evWait *)id.opaque) { + found = 1; + if (prev != NULL) + prev->next = this->next; + else + wl->first = this->next; + if (wl->last == this) + wl->last = prev; + if (wl->first == NULL) + evFreeWaitList(ctx, wl); + break; + } + } + + if (!found) { + /* Maybe it's done */ + for (prev = NULL, this = ctx->waitDone.first; + this != NULL; + prev = this, this = this->next) + if (this == (evWait *)id.opaque) { + found = 1; + if (prev != NULL) + prev->next = this->next; + else + ctx->waitDone.first = this->next; + if (ctx->waitDone.last == this) + ctx->waitDone.last = prev; + break; + } + } + + if (!found) { + errno = ENOENT; + return (-1); + } + + FREE(this); + + if (ctx->debug >= 9) + print_waits(ctx); + + return (0); +} + +int +evDefer(evContext opaqueCtx, evWaitFunc func, void *uap) { + evContext_p *ctx = opaqueCtx.opaque; + evWait *new; + + OKNEW(new); + new->func = func; + new->uap = uap; + new->tag = NULL; + new->next = NULL; + if (ctx->waitDone.last != NULL) + ctx->waitDone.last->next = new; + else + ctx->waitDone.first = new; + ctx->waitDone.last = new; + if (ctx->debug >= 9) + print_waits(ctx); + return (0); +} + +/* Private. */ + +static void +print_waits(evContext_p *ctx) { + evWaitList *wl; + evWait *this; + + evPrintf(ctx, 9, "wait waiting:\n"); + for (wl = ctx->waitLists; wl != NULL; wl = wl->next) { + INSIST(wl->first != NULL); + evPrintf(ctx, 9, " tag %p:", wl->first->tag); + for (this = wl->first; this != NULL; this = this->next) + evPrintf(ctx, 9, " %p", this); + evPrintf(ctx, 9, "\n"); + } + evPrintf(ctx, 9, "wait done:"); + for (this = ctx->waitDone.first; this != NULL; this = this->next) + evPrintf(ctx, 9, " %p", this); + evPrintf(ctx, 9, "\n"); +} + +static evWaitList * +evNewWaitList(evContext_p *ctx) { + evWaitList *new; + + NEW(new); + if (new == NULL) + return (NULL); + new->first = new->last = NULL; + new->prev = NULL; + new->next = ctx->waitLists; + if (new->next != NULL) + new->next->prev = new; + ctx->waitLists = new; + return (new); +} + +static void +evFreeWaitList(evContext_p *ctx, evWaitList *this) { + + INSIST(this != NULL); + + if (this->prev != NULL) + this->prev->next = this->next; + else + ctx->waitLists = this->next; + if (this->next != NULL) + this->next->prev = this->prev; + FREE(this); +} + +static evWaitList * +evGetWaitList(evContext_p *ctx, const void *tag, int should_create) { + evWaitList *this; + + for (this = ctx->waitLists; this != NULL; this = this->next) { + if (this->first != NULL && this->first->tag == tag) + break; + } + if (this == NULL && should_create) + this = evNewWaitList(ctx); + return (this); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/eventlib.c b/usr/src/lib/libresolv2_joy/common/isc/eventlib.c new file mode 100644 index 0000000000..f25c2098ab --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/eventlib.c @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium + * Copyright 2018 Joyent, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* eventlib.c - implement glue for the eventlib + * vix 09sep95 [initial] + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#ifdef SOLARIS2 +#include <limits.h> +#endif /* SOLARIS2 */ + +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> + +#include <isc/eventlib.h> +#include <isc/assertions.h> +#include "eventlib_p.h" + +#include "port_after.h" + +int __evOptMonoTime; + +#ifdef USE_POLL +#define pselect Pselect +#endif /* USE_POLL */ + +/* Forward. */ + +#if defined(NEED_PSELECT) || defined(USE_POLL) +static int pselect(int, void *, void *, void *, + struct timespec *, + const sigset_t *); +#endif + +int __evOptMonoTime; + +/* Public. */ + +int +evCreate(evContext *opaqueCtx) { + evContext_p *ctx; + + /* Make sure the memory heap is initialized. */ + if (meminit(0, 0) < 0 && errno != EEXIST) + return (-1); + + OKNEW(ctx); + + /* Global. */ + ctx->cur = NULL; + + /* Debugging. */ + ctx->debug = 0; + ctx->output = NULL; + + /* Connections. */ + ctx->conns = NULL; + INIT_LIST(ctx->accepts); + + /* Files. */ + ctx->files = NULL; +#ifdef USE_POLL + ctx->pollfds = NULL; + ctx->maxnfds = 0; + ctx->firstfd = 0; + emulMaskInit(ctx, rdLast, EV_READ, 1); + emulMaskInit(ctx, rdNext, EV_READ, 0); + emulMaskInit(ctx, wrLast, EV_WRITE, 1); + emulMaskInit(ctx, wrNext, EV_WRITE, 0); + emulMaskInit(ctx, exLast, EV_EXCEPT, 1); + emulMaskInit(ctx, exNext, EV_EXCEPT, 0); + emulMaskInit(ctx, nonblockBefore, EV_WASNONBLOCKING, 0); +#endif /* USE_POLL */ + FD_ZERO(&ctx->rdNext); + FD_ZERO(&ctx->wrNext); + FD_ZERO(&ctx->exNext); + FD_ZERO(&ctx->nonblockBefore); + ctx->fdMax = -1; + ctx->fdNext = NULL; + ctx->fdCount = 0; /*%< Invalidate {rd,wr,ex}Last. */ +#ifndef USE_POLL + ctx->highestFD = FD_SETSIZE - 1; + memset(ctx->fdTable, 0, sizeof ctx->fdTable); +#else + ctx->highestFD = INT_MAX / sizeof(struct pollfd); + ctx->fdTable = NULL; +#endif /* USE_POLL */ +#ifdef EVENTLIB_TIME_CHECKS + ctx->lastFdCount = 0; +#endif + + /* Streams. */ + ctx->streams = NULL; + ctx->strDone = NULL; + ctx->strLast = NULL; + + /* Timers. */ + ctx->lastEventTime = evNowTime(); +#ifdef EVENTLIB_TIME_CHECKS + ctx->lastSelectTime = ctx->lastEventTime; +#endif + ctx->timers = evCreateTimers(ctx); + if (ctx->timers == NULL) + return (-1); + + /* Waits. */ + ctx->waitLists = NULL; + ctx->waitDone.first = ctx->waitDone.last = NULL; + ctx->waitDone.prev = ctx->waitDone.next = NULL; + + opaqueCtx->opaque = ctx; + return (0); +} + +void +evSetDebug(evContext opaqueCtx, int level, FILE *output) { + evContext_p *ctx = opaqueCtx.opaque; + + ctx->debug = level; + ctx->output = output; +} + +int +evDestroy(evContext opaqueCtx) { + evContext_p *ctx = opaqueCtx.opaque; + int revs = 424242; /*%< Doug Adams. */ + evWaitList *this_wl, *next_wl; + evWait *this_wait, *next_wait; + + /* Connections. */ + while (revs-- > 0 && ctx->conns != NULL) { + evConnID id; + + id.opaque = ctx->conns; + (void) evCancelConn(opaqueCtx, id); + } + INSIST(revs >= 0); + + /* Streams. */ + while (revs-- > 0 && ctx->streams != NULL) { + evStreamID id; + + id.opaque = ctx->streams; + (void) evCancelRW(opaqueCtx, id); + } + + /* Files. */ + while (revs-- > 0 && ctx->files != NULL) { + evFileID id; + + id.opaque = ctx->files; + (void) evDeselectFD(opaqueCtx, id); + } + INSIST(revs >= 0); + + /* Timers. */ + evDestroyTimers(ctx); + + /* Waits. */ + for (this_wl = ctx->waitLists; + revs-- > 0 && this_wl != NULL; + this_wl = next_wl) { + next_wl = this_wl->next; + for (this_wait = this_wl->first; + revs-- > 0 && this_wait != NULL; + this_wait = next_wait) { + next_wait = this_wait->next; + FREE(this_wait); + } + FREE(this_wl); + } + for (this_wait = ctx->waitDone.first; + revs-- > 0 && this_wait != NULL; + this_wait = next_wait) { + next_wait = this_wait->next; + FREE(this_wait); + } + + FREE(ctx); + return (0); +} + +int +evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) { + evContext_p *ctx = opaqueCtx.opaque; + struct timespec nextTime; + evTimer *nextTimer; + evEvent_p *new; + int x, pselect_errno, timerPast; +#ifdef EVENTLIB_TIME_CHECKS + struct timespec interval; +#endif + + /* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */ + x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0); + if (x != 1) + EV_ERR(EINVAL); + + /* Get the time of day. We'll do this again after select() blocks. */ + ctx->lastEventTime = evNowTime(); + + again: + /* Finished accept()'s do not require a select(). */ + if (!EMPTY(ctx->accepts)) { + OKNEW(new); + new->type = Accept; + new->u.accept.this = HEAD(ctx->accepts); + UNLINK(ctx->accepts, HEAD(ctx->accepts), link); + opaqueEv->opaque = new; + return (0); + } + + /* Stream IO does not require a select(). */ + if (ctx->strDone != NULL) { + OKNEW(new); + new->type = Stream; + new->u.stream.this = ctx->strDone; + ctx->strDone = ctx->strDone->nextDone; + if (ctx->strDone == NULL) + ctx->strLast = NULL; + opaqueEv->opaque = new; + return (0); + } + + /* Waits do not require a select(). */ + if (ctx->waitDone.first != NULL) { + OKNEW(new); + new->type = Wait; + new->u.wait.this = ctx->waitDone.first; + ctx->waitDone.first = ctx->waitDone.first->next; + if (ctx->waitDone.first == NULL) + ctx->waitDone.last = NULL; + opaqueEv->opaque = new; + return (0); + } + + /* Get the status and content of the next timer. */ + if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) { + nextTime = nextTimer->due; + timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0); + } else + timerPast = 0; /*%< Make gcc happy. */ + evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount); + if (ctx->fdCount == 0) { + static const struct timespec NoTime = {0, 0L}; + enum { JustPoll, Block, Timer } m; + struct timespec t, *tp; + + /* Are there any events at all? */ + if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1) + EV_ERR(ENOENT); + + /* Figure out what select()'s timeout parameter should be. */ + if ((options & EV_POLL) != 0) { + m = JustPoll; + t = NoTime; + tp = &t; + } else if (nextTimer == NULL) { + m = Block; + /* ``t'' unused. */ + tp = NULL; + } else if (timerPast) { + m = JustPoll; + t = NoTime; + tp = &t; + } else { + m = Timer; + /* ``t'' filled in later. */ + tp = &t; + } +#ifdef EVENTLIB_TIME_CHECKS + if (ctx->debug > 0) { + interval = evSubTime(ctx->lastEventTime, + ctx->lastSelectTime); + if (interval.tv_sec > 0 || interval.tv_nsec > 0) + evPrintf(ctx, 1, + "time between pselect() %u.%09u count %d\n", + interval.tv_sec, interval.tv_nsec, + ctx->lastFdCount); + } +#endif + do { +#ifndef USE_POLL + /* XXX need to copy only the bits we are using. */ + ctx->rdLast = ctx->rdNext; + ctx->wrLast = ctx->wrNext; + ctx->exLast = ctx->exNext; +#else + /* + * The pollfd structure uses separate fields for + * the input and output events (corresponding to + * the ??Next and ??Last fd sets), so there's no + * need to copy one to the other. + */ +#endif /* USE_POLL */ + if (m == Timer) { + INSIST(tp == &t); + t = evSubTime(nextTime, ctx->lastEventTime); + } + + /* XXX should predict system's earliness and adjust. */ + x = pselect(ctx->fdMax+1, + &ctx->rdLast, &ctx->wrLast, &ctx->exLast, + tp, NULL); + pselect_errno = errno; + +#ifndef USE_POLL + evPrintf(ctx, 4, "select() returns %d (err: %s)\n", + x, (x == -1) ? strerror(errno) : "none"); +#else + evPrintf(ctx, 4, "poll() returns %d (err: %s)\n", + x, (x == -1) ? strerror(errno) : "none"); +#endif /* USE_POLL */ + /* Anything but a poll can change the time. */ + if (m != JustPoll) + ctx->lastEventTime = evNowTime(); + + /* Select() likes to finish about 10ms early. */ + } while (x == 0 && m == Timer && + evCmpTime(ctx->lastEventTime, nextTime) < 0); +#ifdef EVENTLIB_TIME_CHECKS + ctx->lastSelectTime = ctx->lastEventTime; +#endif + if (x < 0) { + if (pselect_errno == EINTR) { + if ((options & EV_NULL) != 0) + goto again; + OKNEW(new); + new->type = Null; + /* No data. */ + opaqueEv->opaque = new; + return (0); + } + if (pselect_errno == EBADF) { + for (x = 0; x <= ctx->fdMax; x++) { + struct stat sb; + + if (FD_ISSET(x, &ctx->rdNext) == 0 && + FD_ISSET(x, &ctx->wrNext) == 0 && + FD_ISSET(x, &ctx->exNext) == 0) + continue; + if (fstat(x, &sb) == -1 && + errno == EBADF) + evPrintf(ctx, 1, "EBADF: %d\n", + x); + } + abort(); + } + EV_ERR(pselect_errno); + } + if (x == 0 && (nextTimer == NULL || !timerPast) && + (options & EV_POLL)) + EV_ERR(EWOULDBLOCK); + ctx->fdCount = x; +#ifdef EVENTLIB_TIME_CHECKS + ctx->lastFdCount = x; +#endif + } + INSIST(nextTimer || ctx->fdCount); + + /* Timers go first since we'd like them to be accurate. */ + if (nextTimer && !timerPast) { + /* Has anything happened since we blocked? */ + timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0); + } + if (nextTimer && timerPast) { + OKNEW(new); + new->type = Timer; + new->u.timer.this = nextTimer; + opaqueEv->opaque = new; + return (0); + } + + /* No timers, so there should be a ready file descriptor. */ + x = 0; + while (ctx->fdCount > 0) { + evFile *fid; + int fd, eventmask; + + if (ctx->fdNext == NULL) { + if (++x == 2) { + /* + * Hitting the end twice means that the last + * select() found some FD's which have since + * been deselected. + * + * On some systems, the count returned by + * selects is the total number of bits in + * all masks that are set, and on others it's + * the number of fd's that have some bit set, + * and on others, it's just broken. We + * always assume that it's the number of + * bits set in all masks, because that's what + * the man page says it should do, and + * the worst that can happen is we do an + * extra select(). + */ + ctx->fdCount = 0; + break; + } + ctx->fdNext = ctx->files; + } + fid = ctx->fdNext; + ctx->fdNext = fid->next; + + fd = fid->fd; + eventmask = 0; + if (FD_ISSET(fd, &ctx->rdLast)) + eventmask |= EV_READ; + if (FD_ISSET(fd, &ctx->wrLast)) + eventmask |= EV_WRITE; + if (FD_ISSET(fd, &ctx->exLast)) + eventmask |= EV_EXCEPT; + eventmask &= fid->eventmask; + if (eventmask != 0) { + if ((eventmask & EV_READ) != 0) { + FD_CLR(fd, &ctx->rdLast); + ctx->fdCount--; + } + if ((eventmask & EV_WRITE) != 0) { + FD_CLR(fd, &ctx->wrLast); + ctx->fdCount--; + } + if ((eventmask & EV_EXCEPT) != 0) { + FD_CLR(fd, &ctx->exLast); + ctx->fdCount--; + } + OKNEW(new); + new->type = File; + new->u.file.this = fid; + new->u.file.eventmask = eventmask; + opaqueEv->opaque = new; + return (0); + } + } + if (ctx->fdCount < 0) { + /* + * select()'s count is off on a number of systems, and + * can result in fdCount < 0. + */ + evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount); + ctx->fdCount = 0; + } + + /* We get here if the caller deselect()'s an FD. Gag me with a goto. */ + goto again; +} + +int +evDispatch(evContext opaqueCtx, evEvent opaqueEv) { + evContext_p *ctx = opaqueCtx.opaque; + evEvent_p *ev = opaqueEv.opaque; +#ifdef EVENTLIB_TIME_CHECKS + void *func; + struct timespec start_time; + struct timespec interval; +#endif + +#ifdef EVENTLIB_TIME_CHECKS + if (ctx->debug > 0) + start_time = evNowTime(); +#endif + ctx->cur = ev; + switch (ev->type) { + case Accept: { + evAccept *this = ev->u.accept.this; + + evPrintf(ctx, 5, + "Dispatch.Accept: fd %d -> %d, func %p, uap %p\n", + this->conn->fd, this->fd, + this->conn->func, this->conn->uap); + errno = this->ioErrno; + (this->conn->func)(opaqueCtx, this->conn->uap, this->fd, + &this->la, this->lalen, + &this->ra, this->ralen); +#ifdef EVENTLIB_TIME_CHECKS + func = this->conn->func; +#endif + break; + } + case File: { + evFile *this = ev->u.file.this; + int eventmask = ev->u.file.eventmask; + + evPrintf(ctx, 5, + "Dispatch.File: fd %d, mask 0x%x, func %p, uap %p\n", + this->fd, this->eventmask, this->func, this->uap); + (this->func)(opaqueCtx, this->uap, this->fd, eventmask); +#ifdef EVENTLIB_TIME_CHECKS + func = this->func; +#endif + break; + } + case Stream: { + evStream *this = ev->u.stream.this; + + evPrintf(ctx, 5, + "Dispatch.Stream: fd %d, func %p, uap %p\n", + this->fd, this->func, this->uap); + errno = this->ioErrno; + (this->func)(opaqueCtx, this->uap, this->fd, this->ioDone); +#ifdef EVENTLIB_TIME_CHECKS + func = this->func; +#endif + break; + } + case Timer: { + evTimer *this = ev->u.timer.this; + + evPrintf(ctx, 5, "Dispatch.Timer: func %p, uap %p\n", + this->func, this->uap); + (this->func)(opaqueCtx, this->uap, this->due, this->inter); +#ifdef EVENTLIB_TIME_CHECKS + func = this->func; +#endif + break; + } + case Wait: { + evWait *this = ev->u.wait.this; + + evPrintf(ctx, 5, + "Dispatch.Wait: tag %p, func %p, uap %p\n", + this->tag, this->func, this->uap); + (this->func)(opaqueCtx, this->uap, this->tag); +#ifdef EVENTLIB_TIME_CHECKS + func = this->func; +#endif + break; + } + case Null: { + /* No work. */ +#ifdef EVENTLIB_TIME_CHECKS + func = NULL; +#endif + break; + } + default: { + abort(); + } + } +#ifdef EVENTLIB_TIME_CHECKS + if (ctx->debug > 0) { + interval = evSubTime(evNowTime(), start_time); + /* + * Complain if it took longer than 50 milliseconds. + * + * We call getuid() to make an easy to find mark in a kernel + * trace. + */ + if (interval.tv_sec > 0 || interval.tv_nsec > 50000000) + evPrintf(ctx, 1, + "dispatch interval %u.%09u uid %d type %d func %p\n", + interval.tv_sec, interval.tv_nsec, + getuid(), ev->type, func); + } +#endif + ctx->cur = NULL; + evDrop(opaqueCtx, opaqueEv); + return (0); +} + +void +evDrop(evContext opaqueCtx, evEvent opaqueEv) { + evContext_p *ctx = opaqueCtx.opaque; + evEvent_p *ev = opaqueEv.opaque; + + switch (ev->type) { + case Accept: { + FREE(ev->u.accept.this); + break; + } + case File: { + /* No work. */ + break; + } + case Stream: { + evStreamID id; + + id.opaque = ev->u.stream.this; + (void) evCancelRW(opaqueCtx, id); + break; + } + case Timer: { + evTimer *this = ev->u.timer.this; + evTimerID opaque; + + /* Check to see whether the user func cleared the timer. */ + if (heap_element(ctx->timers, this->index) != this) { + evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n"); + break; + } + /* + * Timer is still there. Delete it if it has expired, + * otherwise set it according to its next interval. + */ + if (this->inter.tv_sec == (time_t)0 && + this->inter.tv_nsec == 0L) { + opaque.opaque = this; + (void) evClearTimer(opaqueCtx, opaque); + } else { + opaque.opaque = this; + (void) evResetTimer(opaqueCtx, opaque, this->func, + this->uap, + evAddTime((this->mode & EV_TMR_RATE) ? + this->due : + ctx->lastEventTime, + this->inter), + this->inter); + } + break; + } + case Wait: { + FREE(ev->u.wait.this); + break; + } + case Null: { + /* No work. */ + break; + } + default: { + abort(); + } + } + FREE(ev); +} + +int +evMainLoop(evContext opaqueCtx) { + evEvent event; + int x; + + while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0) + if ((x = evDispatch(opaqueCtx, event)) < 0) + break; + return (x); +} + +int +evHighestFD(evContext opaqueCtx) { + evContext_p *ctx = opaqueCtx.opaque; + + return (ctx->highestFD); +} + +void +evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (ctx->output != NULL && ctx->debug >= level) { + vfprintf(ctx->output, fmt, ap); + fflush(ctx->output); + } + va_end(ap); +} + +int +evSetOption(evContext *opaqueCtx, const char *option, int value) { + /* evContext_p *ctx = opaqueCtx->opaque; */ + + UNUSED(opaqueCtx); + UNUSED(value); +#ifndef CLOCK_MONOTONIC + UNUSED(option); +#endif + +#ifdef CLOCK_MONOTONIC + if (strcmp(option, "monotime") == 0) { + if (opaqueCtx != NULL) + errno = EINVAL; + if (value == 0 || value == 1) { + __evOptMonoTime = value; + return (0); + } else { + errno = EINVAL; + return (-1); + } + } +#endif + errno = ENOENT; + return (-1); +} + +int +evGetOption(evContext *opaqueCtx, const char *option, int *value) { + /* evContext_p *ctx = opaqueCtx->opaque; */ + + UNUSED(opaqueCtx); +#ifndef CLOCK_MONOTONIC + UNUSED(value); + UNUSED(option); +#endif + +#ifdef CLOCK_MONOTONIC + if (strcmp(option, "monotime") == 0) { + if (opaqueCtx != NULL) + errno = EINVAL; + *value = __evOptMonoTime; + return (0); + } +#endif + errno = ENOENT; + return (-1); +} + +#if defined(NEED_PSELECT) || defined(USE_POLL) +/* XXX needs to move to the porting library. */ +static int +pselect(int nfds, void *rfds, void *wfds, void *efds, + struct timespec *tsp, + const sigset_t *sigmask) +{ + struct timeval tv; + sigset_t sigs; + int n; +#ifdef USE_POLL + int polltimeout = INFTIM; + evContext_p *ctx; + struct pollfd *fds; + nfds_t pnfds; + + UNUSED(nfds); +#else + struct timeval *tvp = NULL; +#endif /* USE_POLL */ + + if (tsp) { + tv = evTimeVal(*tsp); +#ifdef USE_POLL + polltimeout = 1000 * tv.tv_sec + tv.tv_usec / 1000; +#else + tvp = &tv; +#endif /* USE_POLL */ + } + if (sigmask) + sigprocmask(SIG_SETMASK, sigmask, &sigs); +#ifndef USE_POLL + n = select(nfds, rfds, wfds, efds, tvp); +#else + /* + * rfds, wfds, and efds should all be from the same evContext_p, + * so any of them will do. If they're all NULL, the caller is + * presumably calling us to block. + */ + if (rfds != NULL) + ctx = ((__evEmulMask *)rfds)->ctx; + else if (wfds != NULL) + ctx = ((__evEmulMask *)wfds)->ctx; + else if (efds != NULL) + ctx = ((__evEmulMask *)efds)->ctx; + else + ctx = NULL; + if (ctx != NULL && ctx->fdMax != -1) { + fds = &(ctx->pollfds[ctx->firstfd]); + pnfds = ctx->fdMax - ctx->firstfd + 1; + } else { + fds = NULL; + pnfds = 0; + } + n = poll(fds, pnfds, polltimeout); + if (n > 0) { + int i, e; + + INSIST(ctx != NULL); + for (e = 0, i = ctx->firstfd; i <= ctx->fdMax; i++) { + if (ctx->pollfds[i].fd < 0) + continue; + if (FD_ISSET(i, &ctx->rdLast)) + e++; + if (FD_ISSET(i, &ctx->wrLast)) + e++; + if (FD_ISSET(i, &ctx->exLast)) + e++; + } + n = e; + } +#endif /* USE_POLL */ + if (sigmask) + sigprocmask(SIG_SETMASK, &sigs, NULL); + if (tsp) + *tsp = evTimeSpec(tv); + return (n); +} +#endif + +#ifdef USE_POLL +int +evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd) { + + int i, maxnfds; + void *pollfds, *fdTable; + + if (fd < ctx->maxnfds) + return (0); + + /* Don't allow ridiculously small values for pollfd_chunk_size */ + if (pollfd_chunk_size < 20) + pollfd_chunk_size = 20; + + maxnfds = (1 + (fd/pollfd_chunk_size)) * pollfd_chunk_size; + + pollfds = realloc(ctx->pollfds, maxnfds * sizeof(*ctx->pollfds)); + if (pollfds != NULL) + ctx->pollfds = pollfds; + fdTable = realloc(ctx->fdTable, maxnfds * sizeof(*ctx->fdTable)); + if (fdTable != NULL) + ctx->fdTable = fdTable; + + if (pollfds == NULL || fdTable == NULL) { + evPrintf(ctx, 2, "pollfd() realloc (%ld) failed\n", + (long)maxnfds*sizeof(struct pollfd)); + return (-1); + } + + for (i = ctx->maxnfds; i < maxnfds; i++) { + ctx->pollfds[i].fd = -1; + ctx->pollfds[i].events = 0; + ctx->fdTable[i] = 0; + } + + ctx->maxnfds = maxnfds; + + return (0); +} + +/* Find the appropriate 'events' or 'revents' field in the pollfds array */ +short * +__fd_eventfield(int fd, __evEmulMask *maskp) { + + evContext_p *ctx = (evContext_p *)maskp->ctx; + + if (!maskp->result || maskp->type == EV_WASNONBLOCKING) + return (&(ctx->pollfds[fd].events)); + else + return (&(ctx->pollfds[fd].revents)); +} + +/* Translate to poll(2) event */ +short +__poll_event(__evEmulMask *maskp) { + + switch ((maskp)->type) { + case EV_READ: + return (POLLRDNORM); + case EV_WRITE: + return (POLLWRNORM); + case EV_EXCEPT: + return (POLLRDBAND | POLLPRI | POLLWRBAND); + case EV_WASNONBLOCKING: + return (POLLHUP); + default: + return (0); + } +} + +/* + * Clear the events corresponding to the specified mask. If this leaves + * the events mask empty (apart from the POLLHUP bit), set the fd field + * to -1 so that poll(2) will ignore this fd. + */ +void +__fd_clr(int fd, __evEmulMask *maskp) { + + evContext_p *ctx = maskp->ctx; + + *__fd_eventfield(fd, maskp) &= ~__poll_event(maskp); + if ((ctx->pollfds[fd].events & ~POLLHUP) == 0) { + ctx->pollfds[fd].fd = -1; + if (fd == ctx->fdMax) + while (ctx->fdMax > ctx->firstfd && + ctx->pollfds[ctx->fdMax].fd < 0) + ctx->fdMax--; + if (fd == ctx->firstfd) + while (ctx->firstfd <= ctx->fdMax && + ctx->pollfds[ctx->firstfd].fd < 0) + ctx->firstfd++; + /* + * Do we have a empty set of descriptors? + */ + if (ctx->firstfd > ctx->fdMax) { + ctx->fdMax = -1; + ctx->firstfd = 0; + } + } +} + +/* + * Set the events bit(s) corresponding to the specified mask. If the events + * field has any other bits than POLLHUP set, also set the fd field so that + * poll(2) will watch this fd. + */ +void +__fd_set(int fd, __evEmulMask *maskp) { + + evContext_p *ctx = maskp->ctx; + + *__fd_eventfield(fd, maskp) |= __poll_event(maskp); + if ((ctx->pollfds[fd].events & ~POLLHUP) != 0) { + ctx->pollfds[fd].fd = fd; + if (fd < ctx->firstfd || ctx->fdMax == -1) + ctx->firstfd = fd; + if (fd > ctx->fdMax) + ctx->fdMax = fd; + } +} +#endif /* USE_POLL */ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h b/usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h new file mode 100644 index 0000000000..0a3614ab23 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2005 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief private interfaces for eventlib + * \author vix 09sep95 [initial] + * + * $Id: eventlib_p.h,v 1.9 2006/03/09 23:57:56 marka Exp $ + */ + +#ifndef _EVENTLIB_P_H +#define _EVENTLIB_P_H + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/un.h> + +#define EVENTLIB_DEBUG 1 + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/heap.h> +#include <isc/list.h> +#include <isc/memcluster.h> + +#define EV_MASK_ALL (EV_READ | EV_WRITE | EV_EXCEPT) +#define EV_ERR(e) return (errno = (e), -1) +#define OK(x) if ((x) < 0) EV_ERR(errno); else (void)NULL +#define OKFREE(x, y) if ((x) < 0) { FREE((y)); EV_ERR(errno); } \ + else (void)NULL + +#define NEW(p) if (((p) = memget(sizeof *(p))) != NULL) \ + FILL(p); \ + else \ + (void)NULL; +#define OKNEW(p) if (!((p) = memget(sizeof *(p)))) { \ + errno = ENOMEM; \ + return (-1); \ + } else \ + FILL(p) +#define FREE(p) memput((p), sizeof *(p)) + +#if EVENTLIB_DEBUG +#define FILL(p) memset((p), 0xF5, sizeof *(p)) +#else +#define FILL(p) +#endif + +#ifdef USE_POLL +#ifdef HAVE_STROPTS_H +#include <stropts.h> +#endif +#include <poll.h> +#endif /* USE_POLL */ + +typedef struct evConn { + evConnFunc func; + void * uap; + int fd; + int flags; +#define EV_CONN_LISTEN 0x0001 /*%< Connection is a listener. */ +#define EV_CONN_SELECTED 0x0002 /*%< evSelectFD(conn->file). */ +#define EV_CONN_BLOCK 0x0004 /*%< Listener fd was blocking. */ + evFileID file; + struct evConn * prev; + struct evConn * next; +} evConn; + +typedef struct evAccept { + int fd; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } la; + ISC_SOCKLEN_T lalen; + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifndef NO_SOCKADDR_UN + struct sockaddr_un un; +#endif + } ra; + ISC_SOCKLEN_T ralen; + int ioErrno; + evConn * conn; + LINK(struct evAccept) link; +} evAccept; + +typedef struct evFile { + evFileFunc func; + void * uap; + int fd; + int eventmask; + int preemptive; + struct evFile * prev; + struct evFile * next; + struct evFile * fdprev; + struct evFile * fdnext; +} evFile; + +typedef struct evStream { + evStreamFunc func; + void * uap; + evFileID file; + evTimerID timer; + int flags; +#define EV_STR_TIMEROK 0x0001 /*%< IFF timer valid. */ + int fd; + struct iovec * iovOrig; + int iovOrigCount; + struct iovec * iovCur; + int iovCurCount; + int ioTotal; + int ioDone; + int ioErrno; + struct evStream *prevDone, *nextDone; + struct evStream *prev, *next; +} evStream; + +typedef struct evTimer { + evTimerFunc func; + void * uap; + struct timespec due, inter; + int index; + int mode; +#define EV_TMR_RATE 1 +} evTimer; + +typedef struct evWait { + evWaitFunc func; + void * uap; + const void * tag; + struct evWait * next; +} evWait; + +typedef struct evWaitList { + evWait * first; + evWait * last; + struct evWaitList * prev; + struct evWaitList * next; +} evWaitList; + +typedef struct evEvent_p { + enum { Accept, File, Stream, Timer, Wait, Free, Null } type; + union { + struct { evAccept *this; } accept; + struct { evFile *this; int eventmask; } file; + struct { evStream *this; } stream; + struct { evTimer *this; } timer; + struct { evWait *this; } wait; + struct { struct evEvent_p *next; } free; + struct { const void *placeholder; } null; + } u; +} evEvent_p; + +#ifdef USE_POLL +typedef struct { + void *ctx; /* pointer to the evContext_p */ + uint32_t type; /* READ, WRITE, EXCEPT, nonblk */ + uint32_t result; /* 1 => revents, 0 => events */ +} __evEmulMask; + +#define emulMaskInit(ctx, field, ev, lastnext) \ + ctx->field.ctx = ctx; \ + ctx->field.type = ev; \ + ctx->field.result = lastnext; + +extern short *__fd_eventfield(int fd, __evEmulMask *maskp); +extern short __poll_event(__evEmulMask *maskp); +extern void __fd_clr(int fd, __evEmulMask *maskp); +extern void __fd_set(int fd, __evEmulMask *maskp); + +#undef FD_ZERO +#define FD_ZERO(maskp) + +#undef FD_SET +#define FD_SET(fd, maskp) \ + __fd_set(fd, maskp) + +#undef FD_CLR +#define FD_CLR(fd, maskp) \ + __fd_clr(fd, maskp) + +#undef FD_ISSET +#define FD_ISSET(fd, maskp) \ + ((*__fd_eventfield(fd, maskp) & __poll_event(maskp)) != 0) + +#endif /* USE_POLL */ + +typedef struct { + /* Global. */ + const evEvent_p *cur; + /* Debugging. */ + int debug; + FILE *output; + /* Connections. */ + evConn *conns; + LIST(evAccept) accepts; + /* Files. */ + evFile *files, *fdNext; +#ifndef USE_POLL + fd_set rdLast, rdNext; + fd_set wrLast, wrNext; + fd_set exLast, exNext; + fd_set nonblockBefore; + int fdMax, fdCount, highestFD; + evFile *fdTable[FD_SETSIZE]; +#else + struct pollfd *pollfds; /* Allocated as needed */ + evFile **fdTable; /* Ditto */ + int maxnfds; /* # elements in above */ + int firstfd; /* First active fd */ + int fdMax; /* Last active fd */ + int fdCount; /* # fd:s with I/O */ + int highestFD; /* max fd allowed by OS */ + __evEmulMask rdLast, rdNext; + __evEmulMask wrLast, wrNext; + __evEmulMask exLast, exNext; + __evEmulMask nonblockBefore; +#endif /* USE_POLL */ +#ifdef EVENTLIB_TIME_CHECKS + struct timespec lastSelectTime; + int lastFdCount; +#endif + /* Streams. */ + evStream *streams; + evStream *strDone, *strLast; + /* Timers. */ + struct timespec lastEventTime; + heap_context timers; + /* Waits. */ + evWaitList *waitLists; + evWaitList waitDone; +} evContext_p; + +/* eventlib.c */ +#define evPrintf __evPrintf +void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) + ISC_FORMAT_PRINTF(3, 4); + +#ifdef USE_POLL +extern int evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd); +#endif /* USE_POLL */ + +/* ev_timers.c */ +#define evCreateTimers __evCreateTimers +heap_context evCreateTimers(const evContext_p *); +#define evDestroyTimers __evDestroyTimers +void evDestroyTimers(const evContext_p *); + +/* ev_waits.c */ +#define evFreeWait __evFreeWait +evWait *evFreeWait(evContext_p *ctx, evWait *old); + +/* Global options */ +extern int __evOptMonoTime; + +#endif /*_EVENTLIB_P_H*/ diff --git a/usr/src/lib/libresolv2_joy/common/isc/heap.c b/usr/src/lib/libresolv2_joy/common/isc/heap.c new file mode 100644 index 0000000000..3e48d244e2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/heap.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*% + * Heap implementation of priority queues adapted from the following: + * + * _Introduction to Algorithms_, Cormen, Leiserson, and Rivest, + * MIT Press / McGraw Hill, 1990, ISBN 0-262-03141-8, chapter 7. + * + * _Algorithms_, Second Edition, Sedgewick, Addison-Wesley, 1988, + * ISBN 0-201-06673-4, chapter 11. + */ + +#include "port_before.h" + +#include <stddef.h> +#include <stdlib.h> +#include <errno.h> + +#include "port_after.h" + +#include <isc/heap.h> + +/*% + * Note: to make heap_parent and heap_left easy to compute, the first + * element of the heap array is not used; i.e. heap subscripts are 1-based, + * not 0-based. + */ +#define heap_parent(i) ((i) >> 1) +#define heap_left(i) ((i) << 1) + +#define ARRAY_SIZE_INCREMENT 512 + +heap_context +heap_new(heap_higher_priority_func higher_priority, heap_index_func index, + int array_size_increment) { + heap_context ctx; + + if (higher_priority == NULL) + return (NULL); + + ctx = (heap_context)malloc(sizeof (struct heap_context)); + if (ctx == NULL) + return (NULL); + + ctx->array_size = 0; + if (array_size_increment == 0) + ctx->array_size_increment = ARRAY_SIZE_INCREMENT; + else + ctx->array_size_increment = array_size_increment; + ctx->heap_size = 0; + ctx->heap = NULL; + ctx->higher_priority = higher_priority; + ctx->index = index; + return (ctx); +} + +int +heap_free(heap_context ctx) { + if (ctx == NULL) { + errno = EINVAL; + return (-1); + } + + if (ctx->heap != NULL) + free(ctx->heap); + free(ctx); + + return (0); +} + +static int +heap_resize(heap_context ctx) { + void **new_heap; + + ctx->array_size += ctx->array_size_increment; + new_heap = (void **)realloc(ctx->heap, + (ctx->array_size) * (sizeof (void *))); + if (new_heap == NULL) { + errno = ENOMEM; + return (-1); + } + ctx->heap = new_heap; + return (0); +} + +static void +float_up(heap_context ctx, int i, void *elt) { + int p; + + for ( p = heap_parent(i); + i > 1 && ctx->higher_priority(elt, ctx->heap[p]); + i = p, p = heap_parent(i) ) { + ctx->heap[i] = ctx->heap[p]; + if (ctx->index != NULL) + (ctx->index)(ctx->heap[i], i); + } + ctx->heap[i] = elt; + if (ctx->index != NULL) + (ctx->index)(ctx->heap[i], i); +} + +static void +sink_down(heap_context ctx, int i, void *elt) { + int j, size, half_size; + + size = ctx->heap_size; + half_size = size / 2; + while (i <= half_size) { + /* find smallest of the (at most) two children */ + j = heap_left(i); + if (j < size && ctx->higher_priority(ctx->heap[j+1], + ctx->heap[j])) + j++; + if (ctx->higher_priority(elt, ctx->heap[j])) + break; + ctx->heap[i] = ctx->heap[j]; + if (ctx->index != NULL) + (ctx->index)(ctx->heap[i], i); + i = j; + } + ctx->heap[i] = elt; + if (ctx->index != NULL) + (ctx->index)(ctx->heap[i], i); +} + +int +heap_insert(heap_context ctx, void *elt) { + int i; + + if (ctx == NULL || elt == NULL) { + errno = EINVAL; + return (-1); + } + + i = ++ctx->heap_size; + if (ctx->heap_size >= ctx->array_size && heap_resize(ctx) < 0) + return (-1); + + float_up(ctx, i, elt); + + return (0); +} + +int +heap_delete(heap_context ctx, int i) { + void *elt; + int less; + + if (ctx == NULL || i < 1 || i > ctx->heap_size) { + errno = EINVAL; + return (-1); + } + + if (i == ctx->heap_size) { + ctx->heap_size--; + } else { + elt = ctx->heap[ctx->heap_size--]; + less = ctx->higher_priority(elt, ctx->heap[i]); + ctx->heap[i] = elt; + if (less) + float_up(ctx, i, ctx->heap[i]); + else + sink_down(ctx, i, ctx->heap[i]); + } + + return (0); +} + +int +heap_increased(heap_context ctx, int i) { + if (ctx == NULL || i < 1 || i > ctx->heap_size) { + errno = EINVAL; + return (-1); + } + + float_up(ctx, i, ctx->heap[i]); + + return (0); +} + +int +heap_decreased(heap_context ctx, int i) { + if (ctx == NULL || i < 1 || i > ctx->heap_size) { + errno = EINVAL; + return (-1); + } + + sink_down(ctx, i, ctx->heap[i]); + + return (0); +} + +void * +heap_element(heap_context ctx, int i) { + if (ctx == NULL || i < 1 || i > ctx->heap_size) { + errno = EINVAL; + return (NULL); + } + + return (ctx->heap[i]); +} + +int +heap_for_each(heap_context ctx, heap_for_each_func action, void *uap) { + int i; + + if (ctx == NULL || action == NULL) { + errno = EINVAL; + return (-1); + } + + for (i = 1; i <= ctx->heap_size; i++) + (action)(ctx->heap[i], uap); + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/hex.c b/usr/src/lib/libresolv2_joy/common/isc/hex.c new file mode 100644 index 0000000000..e43be4f3b5 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/hex.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2001 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <isc/misc.h> +#include <port_after.h> + +static const char hex[17] = "0123456789abcdef"; + +int +isc_gethexstring(unsigned char *buf, size_t len, int count, FILE *fp, + int *multiline) +{ + int c, n; + unsigned char x; + char *s; + int result = count; + + x = 0; /*%< silence compiler */ + n = 0; + while (count > 0) { + c = fgetc(fp); + + if ((c == EOF) || + (c == '\n' && !*multiline) || + (c == '(' && *multiline) || + (c == ')' && !*multiline)) + goto formerr; + /* comment */ + if (c == ';') { + do { + c = fgetc(fp); + } while (c != EOF && c != '\n'); + if (c == '\n' && *multiline) + continue; + goto formerr; + } + /* white space */ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + /* multiline */ + if ('(' == c || c == ')') { + *multiline = (c == '(' /*)*/); + continue; + } + if ((s = strchr(hex, tolower(c))) == NULL) + goto formerr; + x = (x<<4) | (s - hex); + if (++n == 2) { + if (len > 0U) { + *buf++ = x; + len--; + } else + result = -1; + count--; + n = 0; + } + } + return (result); + + formerr: + if (c == '\n') + ungetc(c, fp); + return (-1); +} + +void +isc_puthexstring(FILE *fp, const unsigned char *buf, size_t buflen, + size_t len1, size_t len2, const char *sep) +{ + size_t i = 0; + + if (len1 < 4U) + len1 = 4; + if (len2 < 4U) + len2 = 4; + while (buflen > 0U) { + fputc(hex[(buf[0]>>4)&0xf], fp); + fputc(hex[buf[0]&0xf], fp); + i += 2; + buflen--; + buf++; + if (i >= len1 && sep != NULL) { + fputs(sep, fp); + i = 0; + len1 = len2; + } + } +} + +void +isc_tohex(const unsigned char *buf, size_t buflen, char *t) { + while (buflen > 0U) { + *t++ = hex[(buf[0]>>4)&0xf]; + *t++ = hex[buf[0]&0xf]; + buf++; + buflen--; + } + *t = '\0'; +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/logging.c b/usr/src/lib/libresolv2_joy/common/isc/logging.c new file mode 100644 index 0000000000..ac4806b4f8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/logging.c @@ -0,0 +1,712 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1996-1999, 2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <syslog.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> + +#include <isc/assertions.h> +#include <isc/logging.h> +#include <isc/memcluster.h> +#include <isc/misc.h> + +#include "port_after.h" + +#include "logging_p.h" + +static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, + LOG_WARNING, LOG_ERR, LOG_CRIT }; + +static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +static const char *level_text[] = { + "info: ", "notice: ", "warning: ", "error: ", "critical: " +}; + +static void +version_rename(log_channel chan) { + unsigned int ver; + char old_name[PATH_MAX+1]; + char new_name[PATH_MAX+1]; + + ver = chan->out.file.versions; + if (ver < 1) + return; + if (ver > LOG_MAX_VERSIONS) + ver = LOG_MAX_VERSIONS; + /* + * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100) + */ + if (strlen(chan->out.file.name) > (size_t)(PATH_MAX-3)) + return; + for (ver--; ver > 0; ver--) { + sprintf(old_name, "%s.%d", chan->out.file.name, ver-1); + sprintf(new_name, "%s.%d", chan->out.file.name, ver); + (void)isc_movefile(old_name, new_name); + } + sprintf(new_name, "%s.0", chan->out.file.name); + (void)isc_movefile(chan->out.file.name, new_name); +} + +FILE * +log_open_stream(log_channel chan) { + FILE *stream; + int fd, flags; + struct stat sb; + int regular; + + if (chan == NULL || chan->type != log_file) { + errno = EINVAL; + return (NULL); + } + + /* + * Don't open already open streams + */ + if (chan->out.file.stream != NULL) + return (chan->out.file.stream); + + if (stat(chan->out.file.name, &sb) < 0) { + if (errno != ENOENT) { + syslog(LOG_ERR, + "log_open_stream: stat of %s failed: %s", + chan->out.file.name, strerror(errno)); + chan->flags |= LOG_CHANNEL_BROKEN; + return (NULL); + } + regular = 1; + } else + regular = (sb.st_mode & S_IFREG); + + if (chan->out.file.versions) { + if (!regular) { + syslog(LOG_ERR, + "log_open_stream: want versions but %s isn't a regular file", + chan->out.file.name); + chan->flags |= LOG_CHANNEL_BROKEN; + errno = EINVAL; + return (NULL); + } + } + + flags = O_WRONLY|O_CREAT|O_APPEND; + + if ((chan->flags & LOG_TRUNCATE) != 0) { + if (regular) { + (void)unlink(chan->out.file.name); + flags |= O_EXCL; + } else { + syslog(LOG_ERR, + "log_open_stream: want truncation but %s isn't a regular file", + chan->out.file.name); + chan->flags |= LOG_CHANNEL_BROKEN; + errno = EINVAL; + return (NULL); + } + } + + fd = open(chan->out.file.name, flags, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (fd < 0) { + syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s", + chan->out.file.name, strerror(errno)); + chan->flags |= LOG_CHANNEL_BROKEN; + return (NULL); + } + stream = fdopen(fd, "a"); + if (stream == NULL) { + syslog(LOG_ERR, "log_open_stream: fdopen() failed"); + chan->flags |= LOG_CHANNEL_BROKEN; + return (NULL); + } + (void) fchown(fd, chan->out.file.owner, chan->out.file.group); + + chan->out.file.stream = stream; + return (stream); +} + +int +log_close_stream(log_channel chan) { + FILE *stream; + + if (chan == NULL || chan->type != log_file) { + errno = EINVAL; + return (0); + } + stream = chan->out.file.stream; + chan->out.file.stream = NULL; + if (stream != NULL && fclose(stream) == EOF) + return (-1); + return (0); +} + +void +log_close_debug_channels(log_context lc) { + log_channel_list lcl; + int i; + + for (i = 0; i < lc->num_categories; i++) + for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl->next) + if (lcl->channel->type == log_file && + lcl->channel->out.file.stream != NULL && + lcl->channel->flags & LOG_REQUIRE_DEBUG) + (void)log_close_stream(lcl->channel); +} + +FILE * +log_get_stream(log_channel chan) { + if (chan == NULL || chan->type != log_file) { + errno = EINVAL; + return (NULL); + } + return (chan->out.file.stream); +} + +char * +log_get_filename(log_channel chan) { + if (chan == NULL || chan->type != log_file) { + errno = EINVAL; + return (NULL); + } + return (chan->out.file.name); +} + +int +log_check_channel(log_context lc, int level, log_channel chan) { + int debugging, chan_level; + + REQUIRE(lc != NULL); + + debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); + + /* + * If not debugging, short circuit debugging messages very early. + */ + if (level > 0 && !debugging) + return (0); + + if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0) + return (0); + + /* Some channels only log when debugging is on. */ + if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging) + return (0); + + /* Some channels use the global level. */ + if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) { + chan_level = lc->level; + } else + chan_level = chan->level; + + if (level > chan_level) + return (0); + + return (1); +} + +int +log_check(log_context lc, int category, int level) { + log_channel_list lcl; + int debugging; + + REQUIRE(lc != NULL); + + debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0); + + /* + * If not debugging, short circuit debugging messages very early. + */ + if (level > 0 && !debugging) + return (0); + + if (category < 0 || category > lc->num_categories) + category = 0; /*%< use default */ + lcl = lc->categories[category]; + if (lcl == NULL) { + category = 0; + lcl = lc->categories[0]; + } + + for ( /* nothing */; lcl != NULL; lcl = lcl->next) { + if (log_check_channel(lc, level, lcl->channel)) + return (1); + } + return (0); +} + +void +log_vwrite(log_context lc, int category, int level, const char *format, + va_list args) { + log_channel_list lcl; + int pri, debugging, did_vsprintf = 0; + int original_category; + FILE *stream; + log_channel chan; + struct timeval tv; + struct tm *local_tm; +#ifdef HAVE_TIME_R + struct tm tm_tmp; +#endif + time_t tt; + const char *category_name; + const char *level_str; + char time_buf[256]; + char level_buf[256]; + + REQUIRE(lc != NULL); + + debugging = (lc->flags & LOG_OPTION_DEBUG); + + /* + * If not debugging, short circuit debugging messages very early. + */ + if (level > 0 && !debugging) + return; + + if (category < 0 || category > lc->num_categories) + category = 0; /*%< use default */ + original_category = category; + lcl = lc->categories[category]; + if (lcl == NULL) { + category = 0; + lcl = lc->categories[0]; + } + + /* + * Get the current time and format it. + */ + time_buf[0]='\0'; + if (gettimeofday(&tv, NULL) < 0) { + syslog(LOG_INFO, "gettimeofday failed in log_vwrite()"); + } else { + tt = tv.tv_sec; +#ifdef HAVE_TIME_R + local_tm = localtime_r(&tt, &tm_tmp); +#else + local_tm = localtime(&tt); +#endif + if (local_tm != NULL) { + sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ", + local_tm->tm_mday, months[local_tm->tm_mon], + local_tm->tm_year+1900, local_tm->tm_hour, + local_tm->tm_min, local_tm->tm_sec, + (long)tv.tv_usec/1000); + } + } + + /* + * Make a string representation of the current category and level + */ + + if (lc->category_names != NULL && + lc->category_names[original_category] != NULL) + category_name = lc->category_names[original_category]; + else + category_name = ""; + + if (level >= log_critical) { + if (level >= 0) { + sprintf(level_buf, "debug %d: ", level); + level_str = level_buf; + } else + level_str = level_text[-level-1]; + } else { + sprintf(level_buf, "level %d: ", level); + level_str = level_buf; + } + + /* + * Write the message to channels. + */ + for ( /* nothing */; lcl != NULL; lcl = lcl->next) { + chan = lcl->channel; + + if (!log_check_channel(lc, level, chan)) + continue; + + if (!did_vsprintf) { + (void)vsprintf(lc->buffer, format, args); + if (strlen(lc->buffer) > (size_t)LOG_BUFFER_SIZE) { + syslog(LOG_CRIT, + "memory overrun in log_vwrite()"); + exit(1); + } + did_vsprintf = 1; + } + + switch (chan->type) { + case log_syslog: + if (level >= log_critical) + pri = (level >= 0) ? 0 : -level; + else + pri = -log_critical; + syslog(chan->out.facility|syslog_priority[pri], + "%s%s%s%s", + (chan->flags & LOG_TIMESTAMP) ? time_buf : "", + (chan->flags & LOG_PRINT_CATEGORY) ? + category_name : "", + (chan->flags & LOG_PRINT_LEVEL) ? + level_str : "", + lc->buffer); + break; + case log_file: + stream = chan->out.file.stream; + if (stream == NULL) { + stream = log_open_stream(chan); + if (stream == NULL) + break; + } + if (chan->out.file.max_size != ULONG_MAX) { + long pos; + + pos = ftell(stream); + if (pos >= 0 && + (unsigned long)pos > + chan->out.file.max_size) { + /* + * try to roll over the log files, + * ignoring all all return codes + * except the open (we don't want + * to write any more anyway) + */ + log_close_stream(chan); + version_rename(chan); + stream = log_open_stream(chan); + if (stream == NULL) + break; + } + } + fprintf(stream, "%s%s%s%s\n", + (chan->flags & LOG_TIMESTAMP) ? time_buf : "", + (chan->flags & LOG_PRINT_CATEGORY) ? + category_name : "", + (chan->flags & LOG_PRINT_LEVEL) ? + level_str : "", + lc->buffer); + fflush(stream); + break; + case log_null: + break; + default: + syslog(LOG_ERR, + "unknown channel type in log_vwrite()"); + } + } +} + +void +log_write(log_context lc, int category, int level, const char *format, ...) { + va_list args; + + va_start(args, format); + log_vwrite(lc, category, level, format, args); + va_end(args); +} + +/*% + * Functions to create, set, or destroy contexts + */ + +int +log_new_context(int num_categories, char **category_names, log_context *lc) { + log_context nlc; + + nlc = memget(sizeof (struct log_context)); + if (nlc == NULL) { + errno = ENOMEM; + return (-1); + } + nlc->num_categories = num_categories; + nlc->category_names = category_names; + nlc->categories = memget(num_categories * sizeof (log_channel_list)); + if (nlc->categories == NULL) { + memput(nlc, sizeof (struct log_context)); + errno = ENOMEM; + return (-1); + } + memset(nlc->categories, '\0', + num_categories * sizeof (log_channel_list)); + nlc->flags = 0U; + nlc->level = 0; + *lc = nlc; + return (0); +} + +void +log_free_context(log_context lc) { + log_channel_list lcl, lcl_next; + log_channel chan; + int i; + + REQUIRE(lc != NULL); + + for (i = 0; i < lc->num_categories; i++) + for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) { + lcl_next = lcl->next; + chan = lcl->channel; + (void)log_free_channel(chan); + memput(lcl, sizeof (struct log_channel_list)); + } + memput(lc->categories, + lc->num_categories * sizeof (log_channel_list)); + memput(lc, sizeof (struct log_context)); +} + +int +log_add_channel(log_context lc, int category, log_channel chan) { + log_channel_list lcl; + + if (lc == NULL || category < 0 || category >= lc->num_categories) { + errno = EINVAL; + return (-1); + } + + lcl = memget(sizeof (struct log_channel_list)); + if (lcl == NULL) { + errno = ENOMEM; + return(-1); + } + lcl->channel = chan; + lcl->next = lc->categories[category]; + lc->categories[category] = lcl; + chan->references++; + return (0); +} + +int +log_remove_channel(log_context lc, int category, log_channel chan) { + log_channel_list lcl, prev_lcl, next_lcl; + int found = 0; + + if (lc == NULL || category < 0 || category >= lc->num_categories) { + errno = EINVAL; + return (-1); + } + + for (prev_lcl = NULL, lcl = lc->categories[category]; + lcl != NULL; + lcl = next_lcl) { + next_lcl = lcl->next; + if (lcl->channel == chan) { + log_free_channel(chan); + if (prev_lcl != NULL) + prev_lcl->next = next_lcl; + else + lc->categories[category] = next_lcl; + memput(lcl, sizeof (struct log_channel_list)); + /* + * We just set found instead of returning because + * the channel might be on the list more than once. + */ + found = 1; + } else + prev_lcl = lcl; + } + if (!found) { + errno = ENOENT; + return (-1); + } + return (0); +} + +int +log_option(log_context lc, int option, int value) { + if (lc == NULL) { + errno = EINVAL; + return (-1); + } + switch (option) { + case LOG_OPTION_DEBUG: + if (value) + lc->flags |= option; + else + lc->flags &= ~option; + break; + case LOG_OPTION_LEVEL: + lc->level = value; + break; + default: + errno = EINVAL; + return (-1); + } + return (0); +} + +int +log_category_is_active(log_context lc, int category) { + if (lc == NULL) { + errno = EINVAL; + return (-1); + } + if (category >= 0 && category < lc->num_categories && + lc->categories[category] != NULL) + return (1); + return (0); +} + +log_channel +log_new_syslog_channel(unsigned int flags, int level, int facility) { + log_channel chan; + + chan = memget(sizeof (struct log_channel)); + if (chan == NULL) { + errno = ENOMEM; + return (NULL); + } + chan->type = log_syslog; + chan->flags = flags; + chan->level = level; + chan->out.facility = facility; + chan->references = 0; + return (chan); +} + +log_channel +log_new_file_channel(unsigned int flags, int level, + const char *name, FILE *stream, unsigned int versions, + unsigned long max_size) { + log_channel chan; + + chan = memget(sizeof (struct log_channel)); + if (chan == NULL) { + errno = ENOMEM; + return (NULL); + } + chan->type = log_file; + chan->flags = flags; + chan->level = level; + if (name != NULL) { + size_t len; + + len = strlen(name); + /* + * Quantize length to a multiple of 256. There's space for the + * NUL, since if len is a multiple of 256, the size chosen will + * be the next multiple. + */ + chan->out.file.name_size = ((len / 256) + 1) * 256; + chan->out.file.name = memget(chan->out.file.name_size); + if (chan->out.file.name == NULL) { + memput(chan, sizeof (struct log_channel)); + errno = ENOMEM; + return (NULL); + } + /* This is safe. */ + strcpy(chan->out.file.name, name); + } else { + chan->out.file.name_size = 0; + chan->out.file.name = NULL; + } + chan->out.file.stream = stream; + chan->out.file.versions = versions; + chan->out.file.max_size = max_size; + chan->out.file.owner = getuid(); + chan->out.file.group = getgid(); + chan->references = 0; + return (chan); +} + +int +log_set_file_owner(log_channel chan, uid_t owner, gid_t group) { + if (chan->type != log_file) { + errno = EBADF; + return (-1); + } + chan->out.file.owner = owner; + chan->out.file.group = group; + return (0); +} + +log_channel +log_new_null_channel() { + log_channel chan; + + chan = memget(sizeof (struct log_channel)); + if (chan == NULL) { + errno = ENOMEM; + return (NULL); + } + chan->type = log_null; + chan->flags = LOG_CHANNEL_OFF; + chan->level = log_info; + chan->references = 0; + return (chan); +} + +int +log_inc_references(log_channel chan) { + if (chan == NULL) { + errno = EINVAL; + return (-1); + } + chan->references++; + return (0); +} + +int +log_dec_references(log_channel chan) { + if (chan == NULL || chan->references <= 0) { + errno = EINVAL; + return (-1); + } + chan->references--; + return (0); +} + +log_channel_type +log_get_channel_type(log_channel chan) { + REQUIRE(chan != NULL); + + return (chan->type); +} + +int +log_free_channel(log_channel chan) { + if (chan == NULL || chan->references <= 0) { + errno = EINVAL; + return (-1); + } + chan->references--; + if (chan->references == 0) { + if (chan->type == log_file) { + if ((chan->flags & LOG_CLOSE_STREAM) && + chan->out.file.stream != NULL) + (void)fclose(chan->out.file.stream); + if (chan->out.file.name != NULL) + memput(chan->out.file.name, + chan->out.file.name_size); + } + memput(chan, sizeof (struct log_channel)); + } + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/logging_p.h b/usr/src/lib/libresolv2_joy/common/isc/logging_p.h new file mode 100644 index 0000000000..5e6314f190 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/logging_p.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef LOGGING_P_H +#define LOGGING_P_H + +typedef struct log_file_desc { + char *name; + size_t name_size; + FILE *stream; + unsigned int versions; + unsigned long max_size; + uid_t owner; + gid_t group; +} log_file_desc; + +typedef union log_output { + int facility; + log_file_desc file; +} log_output; + +struct log_channel { + int level; /*%< don't log messages > level */ + log_channel_type type; + log_output out; + unsigned int flags; + int references; +}; + +typedef struct log_channel_list { + log_channel channel; + struct log_channel_list *next; +} *log_channel_list; + +#define LOG_BUFFER_SIZE 20480 + +struct log_context { + int num_categories; + char **category_names; + log_channel_list *categories; + int flags; + int level; + char buffer[LOG_BUFFER_SIZE]; +}; + +#endif /* !LOGGING_P_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/memcluster.c b/usr/src/lib/libresolv2_joy/common/isc/memcluster.c new file mode 100644 index 0000000000..1a2db0605b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/memcluster.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2005 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + + +/* When this symbol is defined allocations via memget are made slightly + bigger and some debugging info stuck before and after the region given + back to the caller. */ +/* #define DEBUGGING_MEMCLUSTER */ +#define MEMCLUSTER_ATEND + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <isc/memcluster.h> +#include <isc/assertions.h> + +#include "port_after.h" + +#ifdef MEMCLUSTER_RECORD +#ifndef DEBUGGING_MEMCLUSTER +#define DEBUGGING_MEMCLUSTER +#endif +#endif + +#define DEF_MAX_SIZE 1100 +#define DEF_MEM_TARGET 4096 + +typedef u_int32_t fence_t; + +typedef struct { + void * next; +#if defined(DEBUGGING_MEMCLUSTER) +#if defined(MEMCLUSTER_RECORD) + const char * file; + int line; +#endif + size_t size; + fence_t fencepost; +#endif +} memcluster_element; + +#define SMALL_SIZE_LIMIT sizeof(memcluster_element) +#define P_SIZE sizeof(void *) +#define FRONT_FENCEPOST 0xfebafeba +#define BACK_FENCEPOST 0xabefabef +#define FENCEPOST_SIZE 4 + +#ifndef MEMCLUSTER_LITTLE_MALLOC +#define MEMCLUSTER_BIG_MALLOC 1 +#define NUM_BASIC_BLOCKS 64 +#endif + +struct stats { + u_long gets; + u_long totalgets; + u_long blocks; + u_long freefrags; +}; + +#ifdef DO_PTHREADS +#include <pthread.h> +static pthread_mutex_t memlock = PTHREAD_MUTEX_INITIALIZER; +#define MEMLOCK (void)pthread_mutex_lock(&memlock) +#define MEMUNLOCK (void)pthread_mutex_unlock(&memlock) +#else +/* + * Catch bad lock usage in non threaded build. + */ +static unsigned int memlock = 0; +#define MEMLOCK do { INSIST(memlock == 0); memlock = 1; } while (0) +#define MEMUNLOCK do { INSIST(memlock == 1); memlock = 0; } while (0) +#endif /* DO_PTHEADS */ + +/* Private data. */ + +static size_t max_size; +static size_t mem_target; +#ifndef MEMCLUSTER_BIG_MALLOC +static size_t mem_target_half; +static size_t mem_target_fudge; +#endif +static memcluster_element ** freelists; +#ifdef MEMCLUSTER_RECORD +static memcluster_element ** activelists; +#endif +#ifdef MEMCLUSTER_BIG_MALLOC +static memcluster_element * basic_blocks; +#endif +static struct stats * stats; + +/* Forward. */ + +static size_t quantize(size_t); +#if defined(DEBUGGING_MEMCLUSTER) +static void check(unsigned char *, int, size_t); +#endif + +/* Public. */ + +int +meminit(size_t init_max_size, size_t target_size) { + +#if defined(DEBUGGING_MEMCLUSTER) + INSIST(sizeof(fence_t) == FENCEPOST_SIZE); +#endif + if (freelists != NULL) { + errno = EEXIST; + return (-1); + } + if (init_max_size == 0U) + max_size = DEF_MAX_SIZE; + else + max_size = init_max_size; + if (target_size == 0U) + mem_target = DEF_MEM_TARGET; + else + mem_target = target_size; +#ifndef MEMCLUSTER_BIG_MALLOC + mem_target_half = mem_target / 2; + mem_target_fudge = mem_target + mem_target / 4; +#endif + freelists = malloc(max_size * sizeof (memcluster_element *)); + stats = malloc((max_size+1) * sizeof (struct stats)); + if (freelists == NULL || stats == NULL) { + errno = ENOMEM; + return (-1); + } + memset(freelists, 0, + max_size * sizeof (memcluster_element *)); + memset(stats, 0, (max_size + 1) * sizeof (struct stats)); +#ifdef MEMCLUSTER_RECORD + activelists = malloc((max_size + 1) * sizeof (memcluster_element *)); + if (activelists == NULL) { + errno = ENOMEM; + return (-1); + } + memset(activelists, 0, + (max_size + 1) * sizeof (memcluster_element *)); +#endif +#ifdef MEMCLUSTER_BIG_MALLOC + basic_blocks = NULL; +#endif + return (0); +} + +void * +__memget(size_t size) { + return (__memget_record(size, NULL, 0)); +} + +void * +__memget_record(size_t size, const char *file, int line) { + size_t new_size = quantize(size); +#if defined(DEBUGGING_MEMCLUSTER) + memcluster_element *e; + char *p; + fence_t fp = BACK_FENCEPOST; +#endif + void *ret; + + MEMLOCK; + +#if !defined(MEMCLUSTER_RECORD) + UNUSED(file); + UNUSED(line); +#endif + if (freelists == NULL) { + if (meminit(0, 0) == -1) { + MEMUNLOCK; + return (NULL); + } + } + if (size == 0U) { + MEMUNLOCK; + errno = EINVAL; + return (NULL); + } + if (size >= max_size || new_size >= max_size) { + /* memget() was called on something beyond our upper limit. */ + stats[max_size].gets++; + stats[max_size].totalgets++; +#if defined(DEBUGGING_MEMCLUSTER) + e = malloc(new_size); + if (e == NULL) { + MEMUNLOCK; + errno = ENOMEM; + return (NULL); + } + e->next = NULL; + e->size = size; +#ifdef MEMCLUSTER_RECORD + e->file = file; + e->line = line; + e->next = activelists[max_size]; + activelists[max_size] = e; +#endif + MEMUNLOCK; + e->fencepost = FRONT_FENCEPOST; + p = (char *)e + sizeof *e + size; + memcpy(p, &fp, sizeof fp); + return ((char *)e + sizeof *e); +#else + MEMUNLOCK; + return (malloc(size)); +#endif + } + + /* + * If there are no blocks in the free list for this size, get a chunk + * of memory and then break it up into "new_size"-sized blocks, adding + * them to the free list. + */ + if (freelists[new_size] == NULL) { + int i, frags; + size_t total_size; + void *new; + char *curr, *next; + +#ifdef MEMCLUSTER_BIG_MALLOC + if (basic_blocks == NULL) { + new = malloc(NUM_BASIC_BLOCKS * mem_target); + if (new == NULL) { + MEMUNLOCK; + errno = ENOMEM; + return (NULL); + } + curr = new; + next = curr + mem_target; + for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) { + ((memcluster_element *)curr)->next = next; + curr = next; + next += mem_target; + } + /* + * curr is now pointing at the last block in the + * array. + */ + ((memcluster_element *)curr)->next = NULL; + basic_blocks = new; + } + total_size = mem_target; + new = basic_blocks; + basic_blocks = basic_blocks->next; +#else + if (new_size > mem_target_half) + total_size = mem_target_fudge; + else + total_size = mem_target; + new = malloc(total_size); + if (new == NULL) { + MEMUNLOCK; + errno = ENOMEM; + return (NULL); + } +#endif + frags = total_size / new_size; + stats[new_size].blocks++; + stats[new_size].freefrags += frags; + /* Set up a linked-list of blocks of size "new_size". */ + curr = new; + next = curr + new_size; + for (i = 0; i < (frags - 1); i++) { +#if defined (DEBUGGING_MEMCLUSTER) + memset(curr, 0xa5, new_size); +#endif + ((memcluster_element *)curr)->next = next; + curr = next; + next += new_size; + } + /* curr is now pointing at the last block in the array. */ +#if defined (DEBUGGING_MEMCLUSTER) + memset(curr, 0xa5, new_size); +#endif + ((memcluster_element *)curr)->next = freelists[new_size]; + freelists[new_size] = new; + } + + /* The free list uses the "rounded-up" size "new_size". */ +#if defined (DEBUGGING_MEMCLUSTER) + e = freelists[new_size]; + ret = (char *)e + sizeof *e; + /* + * Check to see if this buffer has been written to while on free list. + */ + check(ret, 0xa5, new_size - sizeof *e); + /* + * Mark memory we are returning. + */ + memset(ret, 0xe5, size); +#else + ret = freelists[new_size]; +#endif + freelists[new_size] = freelists[new_size]->next; +#if defined(DEBUGGING_MEMCLUSTER) + e->next = NULL; + e->size = size; + e->fencepost = FRONT_FENCEPOST; +#ifdef MEMCLUSTER_RECORD + e->file = file; + e->line = line; + e->next = activelists[size]; + activelists[size] = e; +#endif + p = (char *)e + sizeof *e + size; + memcpy(p, &fp, sizeof fp); +#endif + + /* + * The stats[] uses the _actual_ "size" requested by the + * caller, with the caveat (in the code above) that "size" >= the + * max. size (max_size) ends up getting recorded as a call to + * max_size. + */ + stats[size].gets++; + stats[size].totalgets++; + stats[new_size].freefrags--; + MEMUNLOCK; +#if defined(DEBUGGING_MEMCLUSTER) + return ((char *)e + sizeof *e); +#else + return (ret); +#endif +} + +/*% + * This is a call from an external caller, + * so we want to count this as a user "put". + */ +void +__memput(void *mem, size_t size) { + __memput_record(mem, size, NULL, 0); +} + +void +__memput_record(void *mem, size_t size, const char *file, int line) { + size_t new_size = quantize(size); +#if defined (DEBUGGING_MEMCLUSTER) + memcluster_element *e; + memcluster_element *el; +#ifdef MEMCLUSTER_RECORD + memcluster_element *prev; +#endif + fence_t fp; + char *p; +#endif + + MEMLOCK; + +#if !defined (MEMCLUSTER_RECORD) + UNUSED(file); + UNUSED(line); +#endif + + REQUIRE(freelists != NULL); + + if (size == 0U) { + MEMUNLOCK; + errno = EINVAL; + return; + } + +#if defined (DEBUGGING_MEMCLUSTER) + e = (memcluster_element *) ((char *)mem - sizeof *e); + INSIST(e->fencepost == FRONT_FENCEPOST); + INSIST(e->size == size); + p = (char *)e + sizeof *e + size; + memcpy(&fp, p, sizeof fp); + INSIST(fp == BACK_FENCEPOST); + INSIST(((u_long)mem % 4) == 0); +#ifdef MEMCLUSTER_RECORD + prev = NULL; + if (size == max_size || new_size >= max_size) + el = activelists[max_size]; + else + el = activelists[size]; + while (el != NULL && el != e) { + prev = el; + el = el->next; + } + INSIST(el != NULL); /*%< double free */ + if (prev == NULL) { + if (size == max_size || new_size >= max_size) + activelists[max_size] = el->next; + else + activelists[size] = el->next; + } else + prev->next = el->next; +#endif +#endif + + if (size == max_size || new_size >= max_size) { + /* memput() called on something beyond our upper limit */ +#if defined(DEBUGGING_MEMCLUSTER) + free(e); +#else + free(mem); +#endif + + INSIST(stats[max_size].gets != 0U); + stats[max_size].gets--; + MEMUNLOCK; + return; + } + + /* The free list uses the "rounded-up" size "new_size": */ +#if defined(DEBUGGING_MEMCLUSTER) + memset(mem, 0xa5, new_size - sizeof *e); /*%< catch write after free */ + e->size = 0; /*%< catch double memput() */ +#ifdef MEMCLUSTER_RECORD + e->file = file; + e->line = line; +#endif +#ifdef MEMCLUSTER_ATEND + e->next = NULL; + el = freelists[new_size]; + while (el != NULL && el->next != NULL) + el = el->next; + if (el) + el->next = e; + else + freelists[new_size] = e; +#else + e->next = freelists[new_size]; + freelists[new_size] = (void *)e; +#endif +#else + ((memcluster_element *)mem)->next = freelists[new_size]; + freelists[new_size] = (memcluster_element *)mem; +#endif + + /* + * The stats[] uses the _actual_ "size" requested by the + * caller, with the caveat (in the code above) that "size" >= the + * max. size (max_size) ends up getting recorded as a call to + * max_size. + */ + INSIST(stats[size].gets != 0U); + stats[size].gets--; + stats[new_size].freefrags++; + MEMUNLOCK; +} + +void * +__memget_debug(size_t size, const char *file, int line) { + void *ptr; + ptr = __memget_record(size, file, line); + fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line, + (u_long)size, ptr); + return (ptr); +} + +void +__memput_debug(void *ptr, size_t size, const char *file, int line) { + fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, ptr, + (u_long)size); + __memput_record(ptr, size, file, line); +} + +/*% + * Print the stats[] on the stream "out" with suitable formatting. + */ +void +memstats(FILE *out) { + size_t i; +#ifdef MEMCLUSTER_RECORD + memcluster_element *e; +#endif + + MEMLOCK; + + if (freelists == NULL) { + MEMUNLOCK; + return; + } + for (i = 1; i <= max_size; i++) { + const struct stats *s = &stats[i]; + + if (s->totalgets == 0U && s->gets == 0U) + continue; + fprintf(out, "%s%5lu: %11lu gets, %11lu rem", + (i == max_size) ? ">=" : " ", + (unsigned long)i, s->totalgets, s->gets); + if (s->blocks != 0U) + fprintf(out, " (%lu bl, %lu ff)", + s->blocks, s->freefrags); + fputc('\n', out); + } +#ifdef MEMCLUSTER_RECORD + fprintf(out, "Active Memory:\n"); + for (i = 1; i <= max_size; i++) { + if ((e = activelists[i]) != NULL) + while (e != NULL) { + fprintf(out, "%s:%d %p:%lu\n", + e->file != NULL ? e->file : + "<UNKNOWN>", e->line, + (char *)e + sizeof *e, + (u_long)e->size); + e = e->next; + } + } +#endif + MEMUNLOCK; +} + +int +memactive(void) { + size_t i; + + if (stats == NULL) + return (0); + for (i = 1; i <= max_size; i++) + if (stats[i].gets != 0U) + return (1); + return (0); +} + +/* Private. */ + +/*% + * Round up size to a multiple of sizeof(void *). This guarantees that a + * block is at least sizeof void *, and that we won't violate alignment + * restrictions, both of which are needed to make lists of blocks. + */ +static size_t +quantize(size_t size) { + int remainder; + /* + * If there is no remainder for the integer division of + * + * (rightsize/P_SIZE) + * + * then we already have a good size; if not, then we need + * to round up the result in order to get a size big + * enough to satisfy the request _and_ aligned on P_SIZE boundaries. + */ + remainder = size % P_SIZE; + if (remainder != 0) + size += P_SIZE - remainder; +#if defined(DEBUGGING_MEMCLUSTER) + return (size + SMALL_SIZE_LIMIT + sizeof (int)); +#else + return (size); +#endif +} + +#if defined(DEBUGGING_MEMCLUSTER) +static void +check(unsigned char *a, int value, size_t len) { + size_t i; + for (i = 0; i < len; i++) + INSIST(a[i] == value); +} +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/movefile.c b/usr/src/lib/libresolv2_joy/common/isc/movefile.c new file mode 100644 index 0000000000..0ffc7047e2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/movefile.c @@ -0,0 +1,43 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2000 by Internet Software Consortium, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + + +#include <port_before.h> +#include <stdio.h> +#include <isc/misc.h> +#include <port_after.h> +#ifndef HAVE_MOVEFILE +/* + * rename() is lame (can't overwrite an existing file) on some systems. + * use movefile() instead, and let lame OS ports do what they need to. + */ + +int +isc_movefile(const char *oldname, const char *newname) { + return (rename(oldname, newname)); +} +#else + static int os_port_has_isc_movefile = 1; +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/isc/tree.c b/usr/src/lib/libresolv2_joy/common/isc/tree.c new file mode 100644 index 0000000000..404014602a --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/tree.c @@ -0,0 +1,530 @@ +/*% + * tree - balanced binary tree library + * + * vix 05apr94 [removed vixie.h dependencies; cleaned up formatting, names] + * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes] + * vix 23jun86 [added delete uar to add for replaced nodes] + * vix 20jun86 [added tree_delete per wirth a+ds (mod2 v.) p. 224] + * vix 06feb86 [added tree_mung()] + * vix 02feb86 [added tree balancing from wirth "a+ds=p" p. 220-221] + * vix 14dec85 [written] + */ + +/*% + * This program text was created by Paul Vixie using examples from the book: + * "Algorithms & Data Structures," Niklaus Wirth, Prentice-Hall, 1986, ISBN + * 0-13-022005-1. Any errors in the conversion from Modula-2 to C are Paul + * Vixie's. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*#define DEBUG "tree"*/ + +#include "port_before.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "port_after.h" + +#include <isc/memcluster.h> +#include <isc/tree.h> + +#ifdef DEBUG +static int debugDepth = 0; +static char *debugFuncs[256]; +# define ENTER(proc) { \ + debugFuncs[debugDepth] = proc; \ + fprintf(stderr, "ENTER(%d:%s.%s)\n", \ + debugDepth, DEBUG, \ + debugFuncs[debugDepth]); \ + debugDepth++; \ + } +# define RET(value) { \ + debugDepth--; \ + fprintf(stderr, "RET(%d:%s.%s)\n", \ + debugDepth, DEBUG, \ + debugFuncs[debugDepth]); \ + return (value); \ + } +# define RETV { \ + debugDepth--; \ + fprintf(stderr, "RETV(%d:%s.%s)\n", \ + debugDepth, DEBUG, \ + debugFuncs[debugDepth]); \ + return; \ + } +# define MSG(msg) fprintf(stderr, "MSG(%s)\n", msg); +#else +# define ENTER(proc) ; +# define RET(value) return (value); +# define RETV return; +# define MSG(msg) ; +#endif + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +static tree * sprout(tree **, tree_t, int *, int (*)(), void (*)()); +static int delete(tree **, int (*)(), tree_t, void (*)(), int *, int *); +static void del(tree **, int *, tree **, void (*)(), int *); +static void bal_L(tree **, int *); +static void bal_R(tree **, int *); + +void +tree_init(tree **ppr_tree) { + ENTER("tree_init") + *ppr_tree = NULL; + RETV +} + +tree_t +tree_srch(tree **ppr_tree, int (*pfi_compare)(tree_t, tree_t), tree_t p_user) { + ENTER("tree_srch") + + if (*ppr_tree) { + int i_comp = (*pfi_compare)(p_user, (**ppr_tree).data); + + if (i_comp > 0) + RET(tree_srch(&(**ppr_tree).right, + pfi_compare, + p_user)) + + if (i_comp < 0) + RET(tree_srch(&(**ppr_tree).left, + pfi_compare, + p_user)) + + /* not higher, not lower... this must be the one. + */ + RET((**ppr_tree).data) + } + + /* grounded. NOT found. + */ + RET(NULL) +} + +tree_t +tree_add(tree **ppr_tree, int (*pfi_compare)(tree_t, tree_t), + tree_t p_user, void (*pfv_uar)()) +{ + int i_balance = FALSE; + + ENTER("tree_add") + if (!sprout(ppr_tree, p_user, &i_balance, pfi_compare, pfv_uar)) + RET(NULL) + RET(p_user) +} + +int +tree_delete(tree **ppr_p, int (*pfi_compare)(tree_t, tree_t), + tree_t p_user, void (*pfv_uar)()) +{ + int i_balance = FALSE, i_uar_called = FALSE; + + ENTER("tree_delete"); + RET(delete(ppr_p, pfi_compare, p_user, pfv_uar, + &i_balance, &i_uar_called)) +} + +int +tree_trav(tree **ppr_tree, int (*pfi_uar)(tree_t)) { + ENTER("tree_trav") + + if (!*ppr_tree) + RET(TRUE) + + if (!tree_trav(&(**ppr_tree).left, pfi_uar)) + RET(FALSE) + if (!(*pfi_uar)((**ppr_tree).data)) + RET(FALSE) + if (!tree_trav(&(**ppr_tree).right, pfi_uar)) + RET(FALSE) + RET(TRUE) +} + +void +tree_mung(tree **ppr_tree, void (*pfv_uar)(tree_t)) { + ENTER("tree_mung") + if (*ppr_tree) { + tree_mung(&(**ppr_tree).left, pfv_uar); + tree_mung(&(**ppr_tree).right, pfv_uar); + if (pfv_uar) + (*pfv_uar)((**ppr_tree).data); + memput(*ppr_tree, sizeof(tree)); + *ppr_tree = NULL; + } + RETV +} + +static tree * +sprout(tree **ppr, tree_t p_data, int *pi_balance, + int (*pfi_compare)(tree_t, tree_t), void (*pfv_delete)(tree_t)) +{ + tree *p1, *p2, *sub; + int cmp; + + ENTER("sprout") + + /* are we grounded? if so, add the node "here" and set the rebalance + * flag, then exit. + */ + if (!*ppr) { + MSG("grounded. adding new node, setting h=true") + *ppr = (tree *) memget(sizeof(tree)); + if (*ppr) { + (*ppr)->left = NULL; + (*ppr)->right = NULL; + (*ppr)->bal = 0; + (*ppr)->data = p_data; + *pi_balance = TRUE; + } + RET(*ppr); + } + + /* compare the data using routine passed by caller. + */ + cmp = (*pfi_compare)(p_data, (*ppr)->data); + + /* if LESS, prepare to move to the left. + */ + if (cmp < 0) { + MSG("LESS. sprouting left.") + sub = sprout(&(*ppr)->left, p_data, pi_balance, + pfi_compare, pfv_delete); + if (sub && *pi_balance) { /*%< left branch has grown */ + MSG("LESS: left branch has grown") + switch ((*ppr)->bal) { + case 1: + /* right branch WAS longer; bal is ok now */ + MSG("LESS: case 1.. bal restored implicitly") + (*ppr)->bal = 0; + *pi_balance = FALSE; + break; + case 0: + /* balance WAS okay; now left branch longer */ + MSG("LESS: case 0.. balnce bad but still ok") + (*ppr)->bal = -1; + break; + case -1: + /* left branch was already too long. rebal */ + MSG("LESS: case -1: rebalancing") + p1 = (*ppr)->left; + if (p1->bal == -1) { /*%< LL */ + MSG("LESS: single LL") + (*ppr)->left = p1->right; + p1->right = *ppr; + (*ppr)->bal = 0; + *ppr = p1; + } else { /*%< double LR */ + MSG("LESS: double LR") + + p2 = p1->right; + p1->right = p2->left; + p2->left = p1; + + (*ppr)->left = p2->right; + p2->right = *ppr; + + if (p2->bal == -1) + (*ppr)->bal = 1; + else + (*ppr)->bal = 0; + + if (p2->bal == 1) + p1->bal = -1; + else + p1->bal = 0; + *ppr = p2; + } /*else*/ + (*ppr)->bal = 0; + *pi_balance = FALSE; + } /*switch*/ + } /*if*/ + RET(sub) + } /*if*/ + + /* if MORE, prepare to move to the right. + */ + if (cmp > 0) { + MSG("MORE: sprouting to the right") + sub = sprout(&(*ppr)->right, p_data, pi_balance, + pfi_compare, pfv_delete); + if (sub && *pi_balance) { + MSG("MORE: right branch has grown") + + switch ((*ppr)->bal) { + case -1: + MSG("MORE: balance was off, fixed implicitly") + (*ppr)->bal = 0; + *pi_balance = FALSE; + break; + case 0: + MSG("MORE: balance was okay, now off but ok") + (*ppr)->bal = 1; + break; + case 1: + MSG("MORE: balance was off, need to rebalance") + p1 = (*ppr)->right; + if (p1->bal == 1) { /*%< RR */ + MSG("MORE: single RR") + (*ppr)->right = p1->left; + p1->left = *ppr; + (*ppr)->bal = 0; + *ppr = p1; + } else { /*%< double RL */ + MSG("MORE: double RL") + + p2 = p1->left; + p1->left = p2->right; + p2->right = p1; + + (*ppr)->right = p2->left; + p2->left = *ppr; + + if (p2->bal == 1) + (*ppr)->bal = -1; + else + (*ppr)->bal = 0; + + if (p2->bal == -1) + p1->bal = 1; + else + p1->bal = 0; + + *ppr = p2; + } /*else*/ + (*ppr)->bal = 0; + *pi_balance = FALSE; + } /*switch*/ + } /*if*/ + RET(sub) + } /*if*/ + + /* not less, not more: this is the same key! replace... + */ + MSG("FOUND: Replacing data value") + *pi_balance = FALSE; + if (pfv_delete) + (*pfv_delete)((*ppr)->data); + (*ppr)->data = p_data; + RET(*ppr) +} + +static int +delete(tree **ppr_p, int (*pfi_compare)(tree_t, tree_t), tree_t p_user, + void (*pfv_uar)(tree_t), int *pi_balance, int *pi_uar_called) +{ + tree *pr_q; + int i_comp, i_ret; + + ENTER("delete") + + if (*ppr_p == NULL) { + MSG("key not in tree") + RET(FALSE) + } + + i_comp = (*pfi_compare)((*ppr_p)->data, p_user); + if (i_comp > 0) { + MSG("too high - scan left") + i_ret = delete(&(*ppr_p)->left, pfi_compare, p_user, pfv_uar, + pi_balance, pi_uar_called); + if (*pi_balance) + bal_L(ppr_p, pi_balance); + } else if (i_comp < 0) { + MSG("too low - scan right") + i_ret = delete(&(*ppr_p)->right, pfi_compare, p_user, pfv_uar, + pi_balance, pi_uar_called); + if (*pi_balance) + bal_R(ppr_p, pi_balance); + } else { + MSG("equal") + pr_q = *ppr_p; + if (pr_q->right == NULL) { + MSG("right subtree null") + *ppr_p = pr_q->left; + *pi_balance = TRUE; + } else if (pr_q->left == NULL) { + MSG("right subtree non-null, left subtree null") + *ppr_p = pr_q->right; + *pi_balance = TRUE; + } else { + MSG("neither subtree null") + del(&pr_q->left, pi_balance, &pr_q, + pfv_uar, pi_uar_called); + if (*pi_balance) + bal_L(ppr_p, pi_balance); + } + if (!*pi_uar_called && pfv_uar) + (*pfv_uar)(pr_q->data); + /* Thanks to wuth@castrov.cuc.ab.ca for the following stmt. */ + memput(pr_q, sizeof(tree)); + i_ret = TRUE; + } + RET(i_ret) +} + +static void +del(tree **ppr_r, int *pi_balance, tree **ppr_q, + void (*pfv_uar)(tree_t), int *pi_uar_called) +{ + ENTER("del") + + if ((*ppr_r)->right != NULL) { + del(&(*ppr_r)->right, pi_balance, ppr_q, + pfv_uar, pi_uar_called); + if (*pi_balance) + bal_R(ppr_r, pi_balance); + } else { + if (pfv_uar) + (*pfv_uar)((*ppr_q)->data); + *pi_uar_called = TRUE; + (*ppr_q)->data = (*ppr_r)->data; + *ppr_q = *ppr_r; + *ppr_r = (*ppr_r)->left; + *pi_balance = TRUE; + } + + RETV +} + +static void +bal_L(tree **ppr_p, int *pi_balance) { + tree *p1, *p2; + int b1, b2; + + ENTER("bal_L") + MSG("left branch has shrunk") + + switch ((*ppr_p)->bal) { + case -1: + MSG("was imbalanced, fixed implicitly") + (*ppr_p)->bal = 0; + break; + case 0: + MSG("was okay, is now one off") + (*ppr_p)->bal = 1; + *pi_balance = FALSE; + break; + case 1: + MSG("was already off, this is too much") + p1 = (*ppr_p)->right; + b1 = p1->bal; + if (b1 >= 0) { + MSG("single RR") + (*ppr_p)->right = p1->left; + p1->left = *ppr_p; + if (b1 == 0) { + MSG("b1 == 0") + (*ppr_p)->bal = 1; + p1->bal = -1; + *pi_balance = FALSE; + } else { + MSG("b1 != 0") + (*ppr_p)->bal = 0; + p1->bal = 0; + } + *ppr_p = p1; + } else { + MSG("double RL") + p2 = p1->left; + b2 = p2->bal; + p1->left = p2->right; + p2->right = p1; + (*ppr_p)->right = p2->left; + p2->left = *ppr_p; + if (b2 == 1) + (*ppr_p)->bal = -1; + else + (*ppr_p)->bal = 0; + if (b2 == -1) + p1->bal = 1; + else + p1->bal = 0; + *ppr_p = p2; + p2->bal = 0; + } + } + RETV +} + +static void +bal_R(tree **ppr_p, int *pi_balance) { + tree *p1, *p2; + int b1, b2; + + ENTER("bal_R") + MSG("right branch has shrunk") + switch ((*ppr_p)->bal) { + case 1: + MSG("was imbalanced, fixed implicitly") + (*ppr_p)->bal = 0; + break; + case 0: + MSG("was okay, is now one off") + (*ppr_p)->bal = -1; + *pi_balance = FALSE; + break; + case -1: + MSG("was already off, this is too much") + p1 = (*ppr_p)->left; + b1 = p1->bal; + if (b1 <= 0) { + MSG("single LL") + (*ppr_p)->left = p1->right; + p1->right = *ppr_p; + if (b1 == 0) { + MSG("b1 == 0") + (*ppr_p)->bal = -1; + p1->bal = 1; + *pi_balance = FALSE; + } else { + MSG("b1 != 0") + (*ppr_p)->bal = 0; + p1->bal = 0; + } + *ppr_p = p1; + } else { + MSG("double LR") + p2 = p1->right; + b2 = p2->bal; + p1->right = p2->left; + p2->left = p1; + (*ppr_p)->left = p2->right; + p2->right = *ppr_p; + if (b2 == -1) + (*ppr_p)->bal = 1; + else + (*ppr_p)->bal = 0; + if (b2 == 1) + p1->bal = -1; + else + p1->bal = 0; + *ppr_p = p2; + p2->bal = 0; + } + } + RETV +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/llib-lresolv_joy b/usr/src/lib/libresolv2_joy/common/llib-lresolv_joy new file mode 100644 index 0000000000..aedd06a0fa --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/llib-lresolv_joy @@ -0,0 +1,59 @@ +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +/* + * Copyright (c) 1997-1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <resolv.h> + +/* + * usr/src/lib/libresolv2 routines + */ + +int dn_skipname(const uchar_t *, const uchar_t *); +void fp_query(const u_char *, FILE *); +const uchar_t * p_cdname(const uchar_t *, const uchar_t *, FILE *); +const char * p_class(int); +void p_query(const u_char *); +const char * p_time(unsigned int); +const char * p_type(int); +void putlong(unsigned int, uchar_t *); +uint32_t _getlong(const u_char *); +uint16_t _getshort(const u_char *); +const char * hstrerror(int); +int res_init(void); +int res_mkquery(int, const char *, int, int, const u_char *, + int, const u_char *, u_char *, int); +int res_query(const char *, int, int, u_char *, int); +int res_querydomain(const char *, const char *, int, int, + u_char *, int); +int res_search(const char *, int, int, u_char *, int); +int res_send(const u_char *, int, u_char *, int); +int res_update(ns_updrec *); +int res_ninit(res_state); +void fp_resstat(const res_state, FILE *); +const char * res_hostalias(const res_state, const char *, char *, size_t); +int res_nquery(res_state, const char *, int, int, u_char *, int); +int res_nsearch(res_state, const char *, int, int, u_char *, int); +int res_nquerydomain(res_state, const char *, const char *, + int, int, u_char *, int); +int res_nmkquery(res_state, int, const char *, int, int, + const u_char *, int, const u_char *, + u_char *, int); +int res_nsend(res_state, const u_char *, int, u_char *, int); +int res_nmkupdate(res_state, ns_updrec *, u_char *, int); +void res_nclose(res_state); +int res_nsendsigned(res_state, const u_char *, int, ns_tsig_key *, + u_char *, int); +int dn_comp(const char *, u_char *, int, u_char **, u_char **); +int dn_expand(const u_char *, const u_char *, const u_char *, + char *, int); diff --git a/usr/src/lib/libresolv2_joy/common/mapfile-vers b/usr/src/lib/libresolv2_joy/common/mapfile-vers new file mode 100644 index 0000000000..e0c66a1c96 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/mapfile-vers @@ -0,0 +1,60 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate { + global: + joy_dn_expand; + joy_res_nsearch; + joy_res_ninit; + joy_res_ndestroy; + joy_res_gethostbyaddr; + joy_res_gethostbyname; + joy_res_gethostbyname2; + joy_res_sethostent; + joy_res_endhostent; + __joy_res_override_retry; + __joy_res_unset_no_hosts_fallback; + __joy_res_set_no_hosts_fallback; + __joy_h_errno; + __joy_ns_get16; + __joy_ns_get32; + local: + *; +}; diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c new file mode 100644 index 0000000000..5802172a03 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static int datepart(const char *, int, int, int, int *); + +/* Public. */ + +/*% + * Convert a date in ASCII into the number of seconds since + * 1 January 1970 (GMT assumed). Format is yyyymmddhhmmss, all + * digits required, no spaces allowed. + */ + +u_int32_t +ns_datetosecs(const char *cp, int *errp) { + struct tm time; + u_int32_t result; + int mdays, i; + static const int days_per_month[12] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if (strlen(cp) != 14U) { + *errp = 1; + return (0); + } + *errp = 0; + + memset(&time, 0, sizeof time); + time.tm_year = datepart(cp + 0, 4, 1990, 9999, errp) - 1900; + time.tm_mon = datepart(cp + 4, 2, 01, 12, errp) - 1; + time.tm_mday = datepart(cp + 6, 2, 01, 31, errp); + time.tm_hour = datepart(cp + 8, 2, 00, 23, errp); + time.tm_min = datepart(cp + 10, 2, 00, 59, errp); + time.tm_sec = datepart(cp + 12, 2, 00, 59, errp); + if (*errp) /*%< Any parse errors? */ + return (0); + + /* + * OK, now because timegm() is not available in all environments, + * we will do it by hand. Roll up sleeves, curse the gods, begin! + */ + +#define SECS_PER_DAY ((u_int32_t)24*60*60) +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + + result = time.tm_sec; /*%< Seconds */ + result += time.tm_min * 60; /*%< Minutes */ + result += time.tm_hour * (60*60); /*%< Hours */ + result += (time.tm_mday - 1) * SECS_PER_DAY; /*%< Days */ + /* Months are trickier. Look without leaping, then leap */ + mdays = 0; + for (i = 0; i < time.tm_mon; i++) + mdays += days_per_month[i]; + result += mdays * SECS_PER_DAY; /*%< Months */ + if (time.tm_mon > 1 && isleap(1900+time.tm_year)) + result += SECS_PER_DAY; /*%< Add leapday for this year */ + /* First figure years without leapdays, then add them in. */ + /* The loop is slow, FIXME, but simple and accurate. */ + result += (time.tm_year - 70) * (SECS_PER_DAY*365); /*%< Years */ + for (i = 70; i < time.tm_year; i++) + if (isleap(1900+i)) + result += SECS_PER_DAY; /*%< Add leapday for prev year */ + return (result); +} + +/* Private. */ + +/*% + * Parse part of a date. Set error flag if any error. + * Don't reset the flag if there is no error. + */ +static int +datepart(const char *buf, int size, int min, int max, int *errp) { + int result = 0; + int i; + + for (i = 0; i < size; i++) { + if (!isdigit((unsigned char)(buf[i]))) + *errp = 1; + result = (result * 10) + buf[i] - '0'; + } + if (result < min) + *errp = 1; + if (result > max) + *errp = 1; + return (result); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c new file mode 100644 index 0000000000..a2bf99f1c1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c @@ -0,0 +1,1149 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <limits.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +#define NS_TYPE_ELT 0x40 /*%< EDNS0 extended label type */ +#define DNS_LABELTYPE_BITSTRING 0x41 + +/* Data. */ + +static const char digits[] = "0123456789"; + +static const char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ +}; + +/* Forward. */ + +static int special(int); +static int printable(int); +static int dn_find(const u_char *, const u_char *, + const u_char * const *, + const u_char * const *); +static int encode_bitsring(const char **, const char *, + unsigned char **, unsigned char **, + unsigned const char *); +static int labellen(const u_char *); +static int decode_bitstring(const unsigned char **, + char *, const char *); + +/* Public. */ + +/*% + * Convert an encoded domain name to printable ascii as per RFC1035. + + * return: + *\li Number of bytes written to buffer, or -1 (with errno set) + * + * notes: + *\li The root is returned as "." + *\li All other domains are returned in non absolute form + */ +int +ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) +{ + const u_char *cp; + char *dn, *eom; + u_char c; + u_int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + if (dn != dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /*%< XXX */ + return (-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { + int m; + + if (n != DNS_LABELTYPE_BITSTRING) { + /* XXX: labellen should reject this case */ + errno = EINVAL; + return (-1); + } + if ((m = decode_bitstring(&cp, dn, eom)) < 0) + { + errno = EMSGSIZE; + return (-1); + } + dn += m; + continue; + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = (char)c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = digits[c / 100]; + *dn++ = digits[(c % 100) / 10]; + *dn++ = digits[c % 10]; + } else { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = (char)c; + } + } + } + if (dn == dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\0'; + return (dn - dst); +} + +/*% + * Convert a ascii string into an encoded domain name as per RFC1035. + * + * return: + * + *\li -1 if it fails + *\li 1 if string was fully qualified + *\li 0 is string was not fully qualified + * + * notes: + *\li Enforces label and domain length limits. + */ +int +ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { + return (ns_name_pton2(src, dst, dstsiz, NULL)); +} + +/* + * ns_name_pton2(src, dst, dstsiz, *dstlen) + * Convert a ascii string into an encoded domain name as per RFC1035. + * return: + * -1 if it fails + * 1 if string was fully qualified + * 0 is string was not fully qualified + * side effects: + * fills in *dstlen (if non-NULL) + * notes: + * Enforces label and domain length limits. + */ +int +ns_name_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen) { + u_char *label, *bp, *eom; + int c, n, escaped, e = 0; + char *cp; + + escaped = 0; + bp = dst; + eom = dst + dstsiz; + label = bp++; + + while ((c = *src++) != 0) { + if (escaped) { + if (c == '[') { /*%< start a bit string label */ + if ((cp = strchr(src, ']')) == NULL) { + errno = EINVAL; /*%< ??? */ + return (-1); + } + if ((e = encode_bitsring(&src, cp + 2, + &label, &bp, eom)) + != 0) { + errno = e; + return (-1); + } + escaped = 0; + label = bp++; + if ((c = *src++) == 0) + goto done; + else if (c != '.') { + errno = EINVAL; + return (-1); + } + continue; + } + else if ((cp = strchr(digits, c)) != NULL) { + n = (cp - digits) * 100; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits) * 10; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits); + if (n > 255) { + errno = EMSGSIZE; + return (-1); + } + c = n; + } + escaped = 0; + } else if (c == '\\') { + escaped = 1; + continue; + } else if (c == '.') { + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */ + errno = EMSGSIZE; + return (-1); + } + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + /* Fully qualified ? */ + if (*src == '\0') { + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = '\0'; + } + if ((bp - dst) > MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + if (dstlen != NULL) + *dstlen = (bp - dst); + return (1); + } + if (c == 0 || *src == '.') { + errno = EMSGSIZE; + return (-1); + } + label = bp++; + continue; + } + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = (u_char)c; + } + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */ + errno = EMSGSIZE; + return (-1); + } + done: + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = 0; + } + if ((bp - dst) > MAXCDNAME) { /*%< src too big */ + errno = EMSGSIZE; + return (-1); + } + if (dstlen != NULL) + *dstlen = (bp - dst); + return (0); +} + +/*% + * Convert a network strings labels into all lowercase. + * + * return: + *\li Number of bytes written to buffer, or -1 (with errno set) + * + * notes: + *\li Enforces label and domain length limits. + */ + +int +ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) +{ + const u_char *cp; + u_char *dn, *eom; + u_char c; + u_int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + *dn++ = n; + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; + return (-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (isascii(c) && isupper(c)) + *dn++ = tolower(c); + else + *dn++ = c; + } + } + *dn++ = '\0'; + return (dn - dst); +} + +/*% + * Unpack a domain name from a message, source may be compressed. + * + * return: + *\li -1 if it fails, or consumed octets if it succeeds. + */ +int +ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, + u_char *dst, size_t dstsiz) +{ + return (ns_name_unpack2(msg, eom, src, dst, dstsiz, NULL)); +} + +/* + * ns_name_unpack2(msg, eom, src, dst, dstsiz, *dstlen) + * Unpack a domain name from a message, source may be compressed. + * return: + * -1 if it fails, or consumed octets if it succeeds. + * side effect: + * fills in *dstlen (if non-NULL). + */ +int +ns_name_unpack2(const u_char *msg, const u_char *eom, const u_char *src, + u_char *dst, size_t dstsiz, size_t *dstlen) +{ + const u_char *srcp, *dstlim; + u_char *dstp; + int n, len, checked, l; + + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + /* Fetch next label in domain name. */ + while ((n = *srcp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: + case NS_TYPE_ELT: + /* Limit checks. */ + if ((l = labellen(srcp - 1)) < 0) { + errno = EMSGSIZE; + return (-1); + } + if (dstp + l + 1 >= dstlim || srcp + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + checked += l + 1; + *dstp++ = n; + memcpy(dstp, srcp, l); + dstp += l; + srcp += l; + break; + + case NS_CMPRSFLGS: + if (srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); + if (srcp < msg || srcp >= eom) { /*%< Out of range. */ + errno = EMSGSIZE; + return (-1); + } + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eom - msg) { + errno = EMSGSIZE; + return (-1); + } + break; + + default: + errno = EMSGSIZE; + return (-1); /*%< flag error */ + } + } + *dstp++ = 0; + if (dstlen != NULL) + *dstlen = dstp - dst; + if (len < 0) + len = srcp - src; + return (len); +} + +/*% + * Pack domain name 'domain' into 'comp_dn'. + * + * return: + *\li Size of the compressed name, or -1. + * + * notes: + *\li 'dnptrs' is an array of pointers to previous compressed names. + *\li dnptrs[0] is a pointer to the beginning of the message. The array + * ends with NULL. + *\li 'lastdnptr' is a pointer to the end of the array pointed to + * by 'dnptrs'. + * + * Side effects: + *\li The list of pointers in dnptrs is updated for labels inserted into + * the message as we compress the name. If 'dnptr' is NULL, we don't + * try to compress names. If 'lastdnptr' is NULL, we don't update the + * list. + */ +int +ns_name_pack(const u_char *src, u_char *dst, int dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char *dstp; + const u_char **cpp, **lpp, *eob, *msg; + const u_char *srcp; + int n, l, first = 1; + + srcp = src; + dstp = dst; + eob = dstp + dstsiz; + lpp = cpp = NULL; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++) + (void)NULL; + lpp = cpp; /*%< end of list to search */ + } + } else + msg = NULL; + + /* make sure the domain we are about to add is legal */ + l = 0; + do { + int l0; + + n = *srcp; + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + errno = EMSGSIZE; + return (-1); + } + if ((l0 = labellen(srcp)) < 0) { + errno = EINVAL; + return (-1); + } + l += l0 + 1; + if (l > MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + srcp += l0 + 1; + } while (n != 0); + + /* from here on we need to reset compression pointer array on error */ + srcp = src; + do { + /* Look to see if we can use pointers. */ + n = *srcp; + if (n != 0 && msg != NULL) { + l = dn_find(srcp, msg, (const u_char * const *)dnptrs, + (const u_char * const *)lpp); + if (l >= 0) { + if (dstp + 1 >= eob) { + goto cleanup; + } + *dstp++ = (l >> 8) | NS_CMPRSFLGS; + *dstp++ = l % 256; + return (dstp - dst); + } + /* Not found, save it. */ + if (lastdnptr != NULL && cpp < lastdnptr - 1 && + (dstp - msg) < 0x4000 && first) { + *cpp++ = dstp; + *cpp = NULL; + first = 0; + } + } + /* copy label to buffer */ + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Should not happen. */ + goto cleanup; + } + n = labellen(srcp); + if (dstp + 1 + n >= eob) { + goto cleanup; + } + memcpy(dstp, srcp, n + 1); + srcp += n + 1; + dstp += n + 1; + } while (n != 0); + + if (dstp > eob) { +cleanup: + if (msg != NULL) + *lpp = NULL; + errno = EMSGSIZE; + return (-1); + } + return (dstp - dst); +} + +/*% + * Expand compressed domain name to presentation format. + * + * return: + *\li Number of bytes read out of `src', or -1 (with errno set). + * + * note: + *\li Root domain returns as "." not "". + */ +int +ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, size_t dstsiz) +{ + u_char tmp[NS_MAXCDNAME]; + int n; + + if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) + return (-1); + if (ns_name_ntop(tmp, dst, dstsiz) == -1) + return (-1); + return (n); +} + +/*% + * Compress a domain name into wire format, using compression pointers. + * + * return: + *\li Number of bytes consumed in `dst' or -1 (with errno set). + * + * notes: + *\li 'dnptrs' is an array of pointers to previous compressed names. + *\li dnptrs[0] is a pointer to the beginning of the message. + *\li The list ends with NULL. 'lastdnptr' is a pointer to the end of the + * array pointed to by 'dnptrs'. Side effect is to update the list of + * pointers for labels inserted into the message as we compress the name. + *\li If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' + * is NULL, we don't update the list. + */ +int +ns_name_compress(const char *src, u_char *dst, size_t dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char tmp[NS_MAXCDNAME]; + + if (ns_name_pton(src, tmp, sizeof tmp) == -1) + return (-1); + return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); +} + +/*% + * Reset dnptrs so that there are no active references to pointers at or + * after src. + */ +void +ns_name_rollback(const u_char *src, const u_char **dnptrs, + const u_char **lastdnptr) +{ + while (dnptrs < lastdnptr && *dnptrs != NULL) { + if (*dnptrs >= src) { + *dnptrs = NULL; + break; + } + dnptrs++; + } +} + +/*% + * Advance *ptrptr to skip over the compressed name it points at. + * + * return: + *\li 0 on success, -1 (with errno set) on failure. + */ +int +ns_name_skip(const u_char **ptrptr, const u_char *eom) +{ + const u_char *cp; + u_int n; + int l; + + cp = *ptrptr; + while (cp < eom && (n = *cp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: /*%< normal case, n == len */ + cp += n; + continue; + case NS_TYPE_ELT: /*%< EDNS0 extended label */ + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /*%< XXX */ + return (-1); + } + cp += l; + continue; + case NS_CMPRSFLGS: /*%< indirection */ + cp++; + break; + default: /*%< illegal type */ + errno = EMSGSIZE; + return (-1); + } + break; + } + if (cp > eom) { + errno = EMSGSIZE; + return (-1); + } + *ptrptr = cp; + return (0); +} + +/* Find the number of octets an nname takes up, including the root label. + * (This is basically ns_name_skip() without compression-pointer support.) + * ((NOTE: can only return zero if passed-in namesiz argument is zero.)) + */ +ssize_t +ns_name_length(ns_nname_ct nname, size_t namesiz) { + ns_nname_ct orig = nname; + u_int n; + + while (namesiz-- > 0 && (n = *nname++) != 0) { + if ((n & NS_CMPRSFLGS) != 0) { + errno = EISDIR; + return (-1); + } + if (n > namesiz) { + errno = EMSGSIZE; + return (-1); + } + nname += n; + namesiz -= n; + } + return (nname - orig); +} + +/* Compare two nname's for equality. Return -1 on error (setting errno). + */ +int +ns_name_eq(ns_nname_ct a, size_t as, ns_nname_ct b, size_t bs) { + ns_nname_ct ae = a + as, be = b + bs; + int ac, bc; + + while (ac = *a, bc = *b, ac != 0 && bc != 0) { + if ((ac & NS_CMPRSFLGS) != 0 || (bc & NS_CMPRSFLGS) != 0) { + errno = EISDIR; + return (-1); + } + if (a + ac >= ae || b + bc >= be) { + errno = EMSGSIZE; + return (-1); + } + if (ac != bc || strncasecmp((const char *) ++a, + (const char *) ++b, ac) != 0) + return (0); + a += ac, b += bc; + } + return (ac == 0 && bc == 0); +} + +/* Is domain "A" owned by (at or below) domain "B"? + */ +int +ns_name_owned(ns_namemap_ct a, int an, ns_namemap_ct b, int bn) { + /* If A is shorter, it cannot be owned by B. */ + if (an < bn) + return (0); + + /* If they are unequal before the length of the shorter, A cannot... */ + while (bn > 0) { + if (a->len != b->len || + strncasecmp((const char *) a->base, + (const char *) b->base, a->len) != 0) + return (0); + a++, an--; + b++, bn--; + } + + /* A might be longer or not, but either way, B owns it. */ + return (1); +} + +/* Build an array of <base,len> tuples from an nname, top-down order. + * Return the number of tuples (labels) thus discovered. + */ +int +ns_name_map(ns_nname_ct nname, size_t namelen, ns_namemap_t map, int mapsize) { + u_int n; + int l; + + n = *nname++; + namelen--; + + /* Root zone? */ + if (n == 0) { + /* Extra data follows name? */ + if (namelen > 0) { + errno = EMSGSIZE; + return (-1); + } + return (0); + } + + /* Compression pointer? */ + if ((n & NS_CMPRSFLGS) != 0) { + errno = EISDIR; + return (-1); + } + + /* Label too long? */ + if (n > namelen) { + errno = EMSGSIZE; + return (-1); + } + + /* Recurse to get rest of name done first. */ + l = ns_name_map(nname + n, namelen - n, map, mapsize); + if (l < 0) + return (-1); + + /* Too many labels? */ + if (l >= mapsize) { + errno = ENAMETOOLONG; + return (-1); + } + + /* We're on our way back up-stack, store current map data. */ + map[l].base = nname; + map[l].len = n; + return (l + 1); +} + +/* Count the labels in a domain name. Root counts, so COM. has two. This + * is to make the result comparable to the result of ns_name_map(). + */ +int +ns_name_labels(ns_nname_ct nname, size_t namesiz) { + int ret = 0; + u_int n; + + while (namesiz-- > 0 && (n = *nname++) != 0) { + if ((n & NS_CMPRSFLGS) != 0) { + errno = EISDIR; + return (-1); + } + if (n > namesiz) { + errno = EMSGSIZE; + return (-1); + } + nname += n; + namesiz -= n; + ret++; + } + return (ret + 1); +} + +/* Private. */ + +/*% + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? + * + * return: + *\li boolean. + */ +static int +special(int ch) { + switch (ch) { + case 0x22: /*%< '"' */ + case 0x2E: /*%< '.' */ + case 0x3B: /*%< ';' */ + case 0x5C: /*%< '\\' */ + case 0x28: /*%< '(' */ + case 0x29: /*%< ')' */ + /* Special modifiers in zone files. */ + case 0x40: /*%< '@' */ + case 0x24: /*%< '$' */ + return (1); + default: + return (0); + } +} + +/*% + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? + * + * return: + *\li boolean. + */ +static int +printable(int ch) { + return (ch > 0x20 && ch < 0x7f); +} + +/*% + * Thinking in noninternationalized USASCII (per the DNS spec), + * convert this character to lower case if it's upper case. + */ +static int +mklower(int ch) { + if (ch >= 0x41 && ch <= 0x5A) + return (ch + 0x20); + return (ch); +} + +/*% + * Search for the counted-label name in an array of compressed names. + * + * return: + *\li offset from msg if found, or -1. + * + * notes: + *\li dnptrs is the pointer to the first name on the list, + *\li not the pointer to the start of the message. + */ +static int +dn_find(const u_char *domain, const u_char *msg, + const u_char * const *dnptrs, + const u_char * const *lastdnptr) +{ + const u_char *dn, *cp, *sp; + const u_char * const *cpp; + u_int n; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) { + sp = *cpp; + /* + * terminate search on: + * root label + * compression pointer + * unusable offset + */ + while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && + (sp - msg) < 0x4000) { + dn = domain; + cp = sp; + while ((n = *cp++) != 0) { + /* + * check for indirection + */ + switch (n & NS_CMPRSFLGS) { + case 0: /*%< normal case, n == len */ + n = labellen(cp - 1); /*%< XXX */ + if (n != *dn++) + goto next; + + for ((void)NULL; n > 0; n--) + if (mklower(*dn++) != + mklower(*cp++)) + goto next; + /* Is next root for both ? */ + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; + case NS_CMPRSFLGS: /*%< indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + break; + + default: /*%< illegal type */ + errno = EMSGSIZE; + return (-1); + } + } + next: ; + sp += *sp + 1; + } + } + errno = ENOENT; + return (-1); +} + +static int +decode_bitstring(const unsigned char **cpp, char *dn, const char *eom) +{ + const unsigned char *cp = *cpp; + char *beg = dn, tc; + int b, blen, plen, i; + + if ((blen = (*cp & 0xff)) == 0) + blen = 256; + plen = (blen + 3) / 4; + plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); + if (dn + plen >= eom) + return (-1); + + cp++; + i = SPRINTF((dn, "\\[x")); + if (i < 0) + return (-1); + dn += i; + for (b = blen; b > 7; b -= 8, cp++) { + i = SPRINTF((dn, "%02x", *cp & 0xff)); + if (i < 0) + return (-1); + dn += i; + } + if (b > 4) { + tc = *cp++; + i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b)))); + if (i < 0) + return (-1); + dn += i; + } else if (b > 0) { + tc = *cp++; + i = SPRINTF((dn, "%1x", + ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); + if (i < 0) + return (-1); + dn += i; + } + i = SPRINTF((dn, "/%d]", blen)); + if (i < 0) + return (-1); + dn += i; + + *cpp = cp; + return (dn - beg); +} + +static int +encode_bitsring(const char **bp, const char *end, unsigned char **labelp, + unsigned char ** dst, unsigned const char *eom) +{ + int afterslash = 0; + const char *cp = *bp; + unsigned char *tp; + char c; + const char *beg_blen; + char *end_blen = NULL; + int value = 0, count = 0, tbcount = 0, blen = 0; + + beg_blen = end_blen = NULL; + + /* a bitstring must contain at least 2 characters */ + if (end - cp < 2) + return (EINVAL); + + /* XXX: currently, only hex strings are supported */ + if (*cp++ != 'x') + return (EINVAL); + if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */ + return (EINVAL); + + for (tp = *dst + 1; cp < end && tp < eom; cp++) { + switch((c = *cp)) { + case ']': /*%< end of the bitstring */ + if (afterslash) { + if (beg_blen == NULL) + return (EINVAL); + blen = (int)strtol(beg_blen, &end_blen, 10); + if (*end_blen != ']') + return (EINVAL); + } + if (count) + *tp++ = ((value << 4) & 0xff); + cp++; /*%< skip ']' */ + goto done; + case '/': + afterslash = 1; + break; + default: + if (afterslash) { + if (!isdigit(c&0xff)) + return (EINVAL); + if (beg_blen == NULL) { + + if (c == '0') { + /* blen never begings with 0 */ + return (EINVAL); + } + beg_blen = cp; + } + } else { + if (!isxdigit(c&0xff)) + return (EINVAL); + value <<= 4; + value += digitvalue[(int)c]; + count += 4; + tbcount += 4; + if (tbcount > 256) + return (EINVAL); + if (count == 8) { + *tp++ = value; + count = 0; + } + } + break; + } + } + done: + if (cp >= end || tp >= eom) + return (EMSGSIZE); + + /* + * bit length validation: + * If a <length> is present, the number of digits in the <bit-data> + * MUST be just sufficient to contain the number of bits specified + * by the <length>. If there are insignificant bits in a final + * hexadecimal or octal digit, they MUST be zero. + * RFC2673, Section 3.2. + */ + if (blen > 0) { + int traillen; + + if (((blen + 3) & ~3) != tbcount) + return (EINVAL); + traillen = tbcount - blen; /*%< between 0 and 3 */ + if (((value << (8 - traillen)) & 0xff) != 0) + return (EINVAL); + } + else + blen = tbcount; + if (blen == 256) + blen = 0; + + /* encode the type and the significant bit fields */ + **labelp = DNS_LABELTYPE_BITSTRING; + **dst = blen; + + *bp = cp; + *dst = tp; + + return (0); +} + +static int +labellen(const u_char *lp) +{ + int bitlen; + u_char l = *lp; + + if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* should be avoided by the caller */ + return (-1); + } + + if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) { + if (l == DNS_LABELTYPE_BITSTRING) { + if ((bitlen = *(lp + 1)) == 0) + bitlen = 256; + return ((bitlen + 7 ) / 8 + 1); + } + return (-1); /*%< unknwon ELT */ + } + return (l); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c new file mode 100644 index 0000000000..f57edeb8fd --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <arpa/nameser.h> + +#include "port_after.h" + +#pragma redefine_extname __ns_get16 __joy_ns_get16 +#pragma redefine_extname __ns_get32 __joy_ns_get32 + +/* Public. */ + +u_int +ns_get16(const u_char *src) { + u_int dst; + + NS_GET16(dst, src); + return (dst); +} + +u_long +ns_get32(const u_char *src) { + u_long dst; + + NS_GET32(dst, src); + return (dst); +} + +void +ns_put16(u_int src, u_char *dst) { + NS_PUT16(src, dst); +} + +void +ns_put32(u_long src, u_char *dst) { + NS_PUT32(src, dst); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c new file mode 100644 index 0000000000..870a4608e2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c @@ -0,0 +1,269 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include <port_before.h> + +#include <arpa/nameser.h> + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include <port_after.h> + +static int rdcpy(ns_newmsg *, ns_type, const u_char *, size_t); + +/* Initialize a "newmsg" object to empty. + */ +int +ns_newmsg_init(u_char *buffer, size_t bufsiz, ns_newmsg *handle) { + ns_msg *msg = &handle->msg; + + memset(handle, 0, sizeof *handle); + msg->_msg = buffer; + msg->_eom = buffer + bufsiz; + msg->_sect = ns_s_qd; + msg->_rrnum = 0; + msg->_msg_ptr = buffer + NS_HFIXEDSZ; + handle->dnptrs[0] = msg->_msg; + handle->dnptrs[1] = NULL; + handle->lastdnptr = &handle->dnptrs[sizeof handle->dnptrs / + sizeof handle->dnptrs[0] - 1]; + return (0); +} + +/* Initialize a "newmsg" object by copying an existing parsed message. + */ +int +ns_newmsg_copy(ns_newmsg *handle, ns_msg *msg) { + ns_flag flag; + ns_sect sect; + + ns_newmsg_id(handle, ns_msg_id(*msg)); + for (flag = ns_f_qr; flag < ns_f_max; flag++) + ns_newmsg_flag(handle, flag, ns_msg_getflag(*msg, flag)); + for (sect = ns_s_qd; sect < ns_s_max; sect++) { + int i, count; + + count = ns_msg_count(*msg, sect); + for (i = 0; i < count; i++) { + ns_rr2 rr; + int x; + + if (ns_parserr2(msg, sect, i, &rr) < 0) + return (-1); + if (sect == ns_s_qd) + x = ns_newmsg_q(handle, + ns_rr_nname(rr), + ns_rr_type(rr), + ns_rr_class(rr)); + else + x = ns_newmsg_rr(handle, sect, + ns_rr_nname(rr), + ns_rr_type(rr), + ns_rr_class(rr), + ns_rr_ttl(rr), + ns_rr_rdlen(rr), + ns_rr_rdata(rr)); + if (x < 0) + return (-1); + } + } + return (0); +} + +/* Set the message-ID in a "newmsg" object. + */ +void +ns_newmsg_id(ns_newmsg *handle, u_int16_t id) { + ns_msg *msg = &handle->msg; + + msg->_id = id; +} + +/* Set a flag (including rcode or opcode) in a "newmsg" object. + */ +void +ns_newmsg_flag(ns_newmsg *handle, ns_flag flag, u_int value) { + extern struct _ns_flagdata _ns_flagdata[16]; + struct _ns_flagdata *fd = &_ns_flagdata[flag]; + ns_msg *msg = &handle->msg; + + assert(flag < ns_f_max); + msg->_flags &= (~fd->mask); + msg->_flags |= (value << fd->shift); +} + +/* Add a question (or zone, if it's an update) to a "newmsg" object. + */ +int +ns_newmsg_q(ns_newmsg *handle, ns_nname_ct qname, + ns_type qtype, ns_class qclass) +{ + ns_msg *msg = &handle->msg; + u_char *t; + int n; + + if (msg->_sect != ns_s_qd) { + errno = ENODEV; + return (-1); + } + t = (u_char *) (unsigned long) msg->_msg_ptr; + if (msg->_rrnum == 0) + msg->_sections[ns_s_qd] = t; + n = ns_name_pack(qname, t, msg->_eom - t, + handle->dnptrs, handle->lastdnptr); + if (n < 0) + return (-1); + t += n; + if (t + QFIXEDSZ >= msg->_eom) { + errno = EMSGSIZE; + return (-1); + } + NS_PUT16(qtype, t); + NS_PUT16(qclass, t); + msg->_msg_ptr = t; + msg->_counts[ns_s_qd] = ++msg->_rrnum; + return (0); +} + +/* Add an RR to a "newmsg" object. + */ +int +ns_newmsg_rr(ns_newmsg *handle, ns_sect sect, + ns_nname_ct name, ns_type type, + ns_class rr_class, u_int32_t ttl, + u_int16_t rdlen, const u_char *rdata) +{ + ns_msg *msg = &handle->msg; + u_char *t; + int n; + + if (sect < msg->_sect) { + errno = ENODEV; + return (-1); + } + t = (u_char *) (unsigned long) msg->_msg_ptr; + if (sect > msg->_sect) { + msg->_sect = sect; + msg->_sections[sect] = t; + msg->_rrnum = 0; + } + n = ns_name_pack(name, t, msg->_eom - t, + handle->dnptrs, handle->lastdnptr); + if (n < 0) + return (-1); + t += n; + if (t + RRFIXEDSZ + rdlen >= msg->_eom) { + errno = EMSGSIZE; + return (-1); + } + NS_PUT16(type, t); + NS_PUT16(rr_class, t); + NS_PUT32(ttl, t); + msg->_msg_ptr = t; + if (rdcpy(handle, type, rdata, rdlen) < 0) + return (-1); + msg->_counts[sect] = ++msg->_rrnum; + return (0); +} + +/* Complete a "newmsg" object and return its size for use in write(). + * (Note: the "newmsg" object is also made ready for ns_parserr() etc.) + */ +size_t +ns_newmsg_done(ns_newmsg *handle) { + ns_msg *msg = &handle->msg; + ns_sect sect; + u_char *t; + + t = (u_char *) (unsigned long) msg->_msg; + NS_PUT16(msg->_id, t); + NS_PUT16(msg->_flags, t); + for (sect = 0; sect < ns_s_max; sect++) + NS_PUT16(msg->_counts[sect], t); + msg->_eom = msg->_msg_ptr; + msg->_sect = ns_s_max; + msg->_rrnum = -1; + msg->_msg_ptr = NULL; + return (msg->_eom - msg->_msg); +} + +/* Private. */ + +/* Copy an RDATA, using compression pointers where RFC1035 permits. + */ +static int +rdcpy(ns_newmsg *handle, ns_type type, const u_char *rdata, size_t rdlen) { + ns_msg *msg = &handle->msg; + u_char *p = (u_char *) (unsigned long) msg->_msg_ptr; + u_char *t = p + NS_INT16SZ; + u_char *s = t; + int n; + + switch (type) { + case ns_t_soa: + /* MNAME. */ + n = ns_name_pack(rdata, t, msg->_eom - t, + handle->dnptrs, handle->lastdnptr); + if (n < 0) + return (-1); + t += n; + if (ns_name_skip(&rdata, msg->_eom) < 0) + return (-1); + + /* ANAME. */ + n = ns_name_pack(rdata, t, msg->_eom - t, + handle->dnptrs, handle->lastdnptr); + if (n < 0) + return (-1); + t += n; + if (ns_name_skip(&rdata, msg->_eom) < 0) + return (-1); + + /* Serial, Refresh, Retry, Expiry, and Minimum. */ + if ((msg->_eom - t) < (NS_INT32SZ * 5)) { + errno = EMSGSIZE; + return (-1); + } + memcpy(t, rdata, NS_INT32SZ * 5); + t += (NS_INT32SZ * 5); + break; + case ns_t_ptr: + case ns_t_cname: + case ns_t_ns: + /* PTRDNAME, CNAME, or NSDNAME. */ + n = ns_name_pack(rdata, t, msg->_eom - t, + handle->dnptrs, handle->lastdnptr); + if (n < 0) + return (-1); + t += n; + break; + default: + memcpy(t, rdata, rdlen); + t += rdlen; + } + NS_PUT16(t - s, p); + msg->_msg_ptr = t; + return (0); +} + diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c new file mode 100644 index 0000000000..a8909c2984 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <string.h> + +#include "port_after.h" + +/* Forward. */ + +static void setsection(ns_msg *msg, ns_sect sect); + +/* Macros. */ + +#if !defined(SOLARIS2) || defined(__COVERITY__) +#define RETERR(err) do { errno = (err); return (-1); } while (0) +#else +#define RETERR(err) \ + do { errno = (err); if (errno == errno) return (-1); } while (0) +#endif + +#define PARSE_FMT_PRESO 0 /* Parse using presentation-format names */ +#define PARSE_FMT_WIRE 1 /* Parse using network-format names */ + +/* Public. */ + +/* These need to be in the same order as the nres.h:ns_flag enum. */ +struct _ns_flagdata _ns_flagdata[16] = { + { 0x8000, 15 }, /*%< qr. */ + { 0x7800, 11 }, /*%< opcode. */ + { 0x0400, 10 }, /*%< aa. */ + { 0x0200, 9 }, /*%< tc. */ + { 0x0100, 8 }, /*%< rd. */ + { 0x0080, 7 }, /*%< ra. */ + { 0x0040, 6 }, /*%< z. */ + { 0x0020, 5 }, /*%< ad. */ + { 0x0010, 4 }, /*%< cd. */ + { 0x000f, 0 }, /*%< rcode. */ + { 0x0000, 0 }, /*%< expansion (1/6). */ + { 0x0000, 0 }, /*%< expansion (2/6). */ + { 0x0000, 0 }, /*%< expansion (3/6). */ + { 0x0000, 0 }, /*%< expansion (4/6). */ + { 0x0000, 0 }, /*%< expansion (5/6). */ + { 0x0000, 0 }, /*%< expansion (6/6). */ +}; + +int ns_msg_getflag(ns_msg handle, int flag) { + return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift); +} + +int +ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) { + const u_char *optr = ptr; + + for ((void)NULL; count > 0; count--) { + int b, rdlength; + + b = dn_skipname(ptr, eom); + if (b < 0) + RETERR(EMSGSIZE); + ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/; + if (section != ns_s_qd) { + if (ptr + NS_INT32SZ + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + ptr += NS_INT32SZ/*TTL*/; + NS_GET16(rdlength, ptr); + ptr += rdlength/*RData*/; + } + } + if (ptr > eom) + RETERR(EMSGSIZE); + return (ptr - optr); +} + +int +ns_initparse(const u_char *msg, int msglen, ns_msg *handle) { + const u_char *eom = msg + msglen; + int i; + + handle->_msg = msg; + handle->_eom = eom; + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_id, msg); + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_flags, msg); + for (i = 0; i < ns_s_max; i++) { + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_counts[i], msg); + } + for (i = 0; i < ns_s_max; i++) + if (handle->_counts[i] == 0) + handle->_sections[i] = NULL; + else { + int b = ns_skiprr(msg, eom, (ns_sect)i, + handle->_counts[i]); + + if (b < 0) + return (-1); + handle->_sections[i] = msg; + msg += b; + } + if (msg != eom) + RETERR(EMSGSIZE); + setsection(handle, ns_s_max); + return (0); +} + +int +ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) { + int b; + int tmp; + + /* Make section right. */ + tmp = section; + if (tmp < 0 || section >= ns_s_max) + RETERR(ENODEV); + if (section != handle->_sect) + setsection(handle, section); + + /* Make rrnum right. */ + if (rrnum == -1) + rrnum = handle->_rrnum; + if (rrnum < 0 || rrnum >= handle->_counts[(int)section]) + RETERR(ENODEV); + if (rrnum < handle->_rrnum) + setsection(handle, section); + if (rrnum > handle->_rrnum) { + b = ns_skiprr(handle->_msg_ptr, handle->_eom, section, + rrnum - handle->_rrnum); + + if (b < 0) + return (-1); + handle->_msg_ptr += b; + handle->_rrnum = rrnum; + } + + /* Do the parse. */ + b = dn_expand(handle->_msg, handle->_eom, + handle->_msg_ptr, rr->name, NS_MAXDNAME); + if (b < 0) + return (-1); + handle->_msg_ptr += b; + if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET16(rr->type, handle->_msg_ptr); + NS_GET16(rr->rr_class, handle->_msg_ptr); + if (section == ns_s_qd) { + rr->ttl = 0; + rr->rdlength = 0; + rr->rdata = NULL; + } else { + if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET32(rr->ttl, handle->_msg_ptr); + NS_GET16(rr->rdlength, handle->_msg_ptr); + if (handle->_msg_ptr + rr->rdlength > handle->_eom) + RETERR(EMSGSIZE); + rr->rdata = handle->_msg_ptr; + handle->_msg_ptr += rr->rdlength; + } + if (++handle->_rrnum > handle->_counts[(int)section]) + setsection(handle, (ns_sect)((int)section + 1)); + + /* All done. */ + return (0); +} + +/* + * This is identical to the above but uses network-format (uncompressed) names. + */ +int +ns_parserr2(ns_msg *handle, ns_sect section, int rrnum, ns_rr2 *rr) { + int b; + int tmp; + + /* Make section right. */ + if ((tmp = section) < 0 || section >= ns_s_max) + RETERR(ENODEV); + if (section != handle->_sect) + setsection(handle, section); + + /* Make rrnum right. */ + if (rrnum == -1) + rrnum = handle->_rrnum; + if (rrnum < 0 || rrnum >= handle->_counts[(int)section]) + RETERR(ENODEV); + if (rrnum < handle->_rrnum) + setsection(handle, section); + if (rrnum > handle->_rrnum) { + b = ns_skiprr(handle->_msg_ptr, handle->_eom, section, + rrnum - handle->_rrnum); + + if (b < 0) + return (-1); + handle->_msg_ptr += b; + handle->_rrnum = rrnum; + } + + /* Do the parse. */ + b = ns_name_unpack2(handle->_msg, handle->_eom, handle->_msg_ptr, + rr->nname, NS_MAXNNAME, &rr->nnamel); + if (b < 0) + return (-1); + handle->_msg_ptr += b; + if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET16(rr->type, handle->_msg_ptr); + NS_GET16(rr->rr_class, handle->_msg_ptr); + if (section == ns_s_qd) { + rr->ttl = 0; + rr->rdlength = 0; + rr->rdata = NULL; + } else { + if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET32(rr->ttl, handle->_msg_ptr); + NS_GET16(rr->rdlength, handle->_msg_ptr); + if (handle->_msg_ptr + rr->rdlength > handle->_eom) + RETERR(EMSGSIZE); + rr->rdata = handle->_msg_ptr; + handle->_msg_ptr += rr->rdlength; + } + if (++handle->_rrnum > handle->_counts[(int)section]) + setsection(handle, (ns_sect)((int)section + 1)); + + /* All done. */ + return (0); +} + +/* Private. */ + +static void +setsection(ns_msg *msg, ns_sect sect) { + msg->_sect = sect; + if (sect == ns_s_max) { + msg->_rrnum = -1; + msg->_msg_ptr = NULL; + } else { + msg->_rrnum = 0; + msg->_msg_ptr = msg->_sections[(int)sect]; + } +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c new file mode 100644 index 0000000000..2624c63732 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c @@ -0,0 +1,1239 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * Copyright 2018 Joyent, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <isc/assertions.h> +#include <isc/dst.h> +#include <errno.h> +#include <resolv_joy.h> +#include <string.h> +#include <ctype.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static size_t prune_origin(const char *name, const char *origin); +static int charstr(const u_char *rdata, const u_char *edata, + char **buf, size_t *buflen); +static int addname(const u_char *msg, size_t msglen, + const u_char **p, const char *origin, + char **buf, size_t *buflen); +static void addlen(size_t len, char **buf, size_t *buflen); +static int addstr(const char *src, size_t len, + char **buf, size_t *buflen); +static int addtab(size_t len, size_t target, int spaced, + char **buf, size_t *buflen); + +/* Macros. */ + +#define T(x) \ + do { \ + if ((x) < 0) \ + return (-1); \ + } while (0) + +static const char base32hex[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUV=0123456789abcdefghijklmnopqrstuv"; + +/* Public. */ + +/*% + * Convert an RR to presentation format. + * + * return: + *\li Number of characters written to buf, or -1 (check errno). + */ +int +ns_sprintrr(const ns_msg *handle, const ns_rr *rr, + const char *name_ctx, const char *origin, + char *buf, size_t buflen) +{ + int n; + + n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle), + ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr), + ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr), + name_ctx, origin, buf, buflen); + return (n); +} + +/*% + * Convert the fields of an RR into presentation format. + * + * return: + *\li Number of characters written to buf, or -1 (check errno). + */ +int +ns_sprintrrf(const u_char *msg, size_t msglen, + const char *name, ns_class class, ns_type type, + u_long ttl, const u_char *rdata, size_t rdlen, + const char *name_ctx, const char *origin, + char *buf, size_t buflen) +{ + const char *obuf = buf; + const u_char *edata = rdata + rdlen; + int spaced = 0; + + const char *comment; + char tmp[100]; + int len, x; + + /* + * Owner. + */ + if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) { + T(addstr("\t\t\t", 3, &buf, &buflen)); + } else { + len = prune_origin(name, origin); + if (*name == '\0') { + goto root; + } else if (len == 0) { + T(addstr("@\t\t\t", 4, &buf, &buflen)); + } else { + T(addstr(name, len, &buf, &buflen)); + /* Origin not used or not root, and no trailing dot? */ + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + name[len] == '\0')) && name[len - 1] != '.') { + root: + T(addstr(".", 1, &buf, &buflen)); + len++; + } + T(spaced = addtab(len, 24, spaced, &buf, &buflen)); + } + } + + /* + * TTL, Class, Type. + */ + T(x = ns_format_ttl(ttl, buf, buflen)); + addlen(x, &buf, &buflen); + len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type))); + T(addstr(tmp, len, &buf, &buflen)); + T(spaced = addtab(x + len, 16, spaced, &buf, &buflen)); + + /* + * RData. + */ + switch (type) { + case ns_t_a: + if (rdlen != (size_t)NS_INADDRSZ) + goto formerr; + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + break; + + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ns: + case ns_t_ptr: + case ns_t_dname: + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + + case ns_t_hinfo: + case ns_t_isdn: + /* First word. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + + /* Second word, optional in ISDN records. */ + if (type == ns_t_isdn && rdata == edata) + break; + + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + break; + + case ns_t_soa: { + u_long t; + + /* Server name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Administrator name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" (\n", 3, &buf, &buflen)); + spaced = 0; + + if ((edata - rdata) != 5*NS_INT32SZ) + goto formerr; + + /* Serial number. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + len = SPRINTF((tmp, "%lu", t)); + T(addstr(tmp, len, &buf, &buflen)); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; serial\n", 9, &buf, &buflen)); + spaced = 0; + + /* Refresh interval. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; refresh\n", 10, &buf, &buflen)); + spaced = 0; + + /* Retry interval. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; retry\n", 8, &buf, &buflen)); + spaced = 0; + + /* Expiry. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; expiry\n", 9, &buf, &buflen)); + spaced = 0; + + /* Minimum TTL. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(addstr(" )", 2, &buf, &buflen)); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; minimum\n", 10, &buf, &buflen)); + + break; + } + + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: + case ns_t_kx: { + u_int t; + + if (rdlen < (size_t)NS_INT16SZ) + goto formerr; + + /* Priority. */ + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Target. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_px: { + u_int t; + + if (rdlen < (size_t)NS_INT16SZ) + goto formerr; + + /* Priority. */ + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Name1. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Name2. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_x25: + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + break; + + case ns_t_txt: + case ns_t_spf: + while (rdata < edata) { + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + if (rdata < edata) + T(addstr(" ", 1, &buf, &buflen)); + } + break; + + case ns_t_nsap: { + char t[2+255*3]; + + (void) inet_nsap_ntoa(rdlen, rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; + } + + case ns_t_aaaa: + if (rdlen != (size_t)NS_IN6ADDRSZ) + goto formerr; + (void) inet_ntop(AF_INET6, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + break; + + case ns_t_loc: { + char t[255]; + + /* XXX protocol format checking? */ + (void) loc_ntoa(rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; + } + + case ns_t_naptr: { + u_int order, preference; + char t[50]; + + if (rdlen < 2U*NS_INT16SZ) + goto formerr; + + /* Order, Precedence. */ + order = ns_get16(rdata); rdata += NS_INT16SZ; + preference = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((t, "%u %u ", order, preference)); + T(addstr(t, len, &buf, &buflen)); + + /* Flags. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Service. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Regexp. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len < 0) + return (-1); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Server. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + case ns_t_srv: { + u_int priority, weight, port; + char t[50]; + + if (rdlen < 3U*NS_INT16SZ) + goto formerr; + + /* Priority, Weight, Port. */ + priority = ns_get16(rdata); rdata += NS_INT16SZ; + weight = ns_get16(rdata); rdata += NS_INT16SZ; + port = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((t, "%u %u %u ", priority, weight, port)); + T(addstr(t, len, &buf, &buflen)); + + /* Server. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + case ns_t_minfo: + case ns_t_rp: + /* Name1. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Name2. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + + case ns_t_wks: { + int n, lcnt; + + if (rdlen < 1U + NS_INT32SZ) + goto formerr; + + /* Address. */ + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += NS_INADDRSZ; + + /* Protocol. */ + len = SPRINTF((tmp, " %u ( ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata += NS_INT8SZ; + + /* Bit map. */ + n = 0; + lcnt = 0; + while (rdata < edata) { + u_int c = *rdata++; + do { + if (c & 0200) { + if (lcnt == 0) { + T(addstr("\n\t\t\t\t", 5, + &buf, &buflen)); + lcnt = 10; + spaced = 0; + } + len = SPRINTF((tmp, "%d ", n)); + T(addstr(tmp, len, &buf, &buflen)); + lcnt--; + } + c <<= 1; + } while (++n & 07); + } + T(addstr(")", 1, &buf, &buflen)); + + break; + } + + case ns_t_key: + case ns_t_dnskey: { + char base64_key[NS_MD5RSA_MAX_BASE64]; + u_int keyflags, protocol, algorithm, key_id; + const char *leader; + int n; + + if (rdlen < 0U + NS_INT16SZ + NS_INT8SZ + NS_INT8SZ) + goto formerr; + + /* Key flags, Protocol, Algorithm. */ + key_id = dst_s_dns_key_id(rdata, edata-rdata); + keyflags = ns_get16(rdata); rdata += NS_INT16SZ; + protocol = *rdata++; + algorithm = *rdata++; + len = SPRINTF((tmp, "0x%04x %u %u", + keyflags, protocol, algorithm)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Public key data. */ + len = b64_ntop(rdata, edata - rdata, + base64_key, sizeof base64_key); + if (len < 0) + goto formerr; + if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } else + leader = " "; + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + n = SPRINTF((tmp, " ; key_tag= %u", key_id)); + T(addstr(tmp, n, &buf, &buflen)); + + break; + } + + case ns_t_sig: + case ns_t_rrsig: { + char base64_key[NS_MD5RSA_MAX_BASE64]; + u_int type, algorithm, labels, footprint; + const char *leader; + u_long t; + int n; + + if (rdlen < 22U) + goto formerr; + + /* Type covered, Algorithm, Label count, Original TTL. */ + type = ns_get16(rdata); rdata += NS_INT16SZ; + algorithm = *rdata++; + labels = *rdata++; + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s %d %d %lu ", + p_type(type), algorithm, labels, t)); + T(addstr(tmp, len, &buf, &buflen)); + if (labels > (u_int)dn_count_labels(name)) + goto formerr; + + /* Signature expiry. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Time signed. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Signature Footprint. */ + footprint = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", footprint)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Signer's name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + /* Signature. */ + len = b64_ntop(rdata, edata - rdata, + base64_key, sizeof base64_key); + if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } else + leader = " "; + if (len < 0) + goto formerr; + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + break; + } + + case ns_t_nxt: { + int n, c; + + /* Next domain name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + /* Type bit map. */ + n = edata - rdata; + for (c = 0; c < n*8; c++) + if (NS_NXT_BIT_ISSET(c, rdata)) { + len = SPRINTF((tmp, " %s", p_type(c))); + T(addstr(tmp, len, &buf, &buflen)); + } + break; + } + + case ns_t_cert: { + u_int c_type, key_tag, alg; + int n; + unsigned int siz; + char base64_cert[8192], tmp[40]; + const char *leader; + + c_type = ns_get16(rdata); rdata += NS_INT16SZ; + key_tag = ns_get16(rdata); rdata += NS_INT16SZ; + alg = (u_int) *rdata++; + + len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg)); + T(addstr(tmp, len, &buf, &buflen)); + siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ + if (siz > sizeof(base64_cert) * 3/4) { + const char *str = "record too long to print"; + T(addstr(str, strlen(str), &buf, &buflen)); + } + else { + len = b64_ntop(rdata, edata-rdata, base64_cert, siz); + + if (len < 0) + goto formerr; + else if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } + else + leader = " "; + + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), + &buf, &buflen)); + T(addstr(base64_cert + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + } + break; + } + + case ns_t_tkey: { + /* KJD - need to complete this */ + u_long t; + int mode, err, keysize; + + /* Algorithm name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Inception. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Experation. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Mode , Error, Key Size. */ + /* Priority, Weight, Port. */ + mode = ns_get16(rdata); rdata += NS_INT16SZ; + err = ns_get16(rdata); rdata += NS_INT16SZ; + keysize = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize)); + T(addstr(tmp, len, &buf, &buflen)); + + /* XXX need to dump key, print otherdata length & other data */ + break; + } + + case ns_t_tsig: { + /* BEW - need to complete this */ + int n; + + T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + rdata += 8; /*%< time */ + n = ns_get16(rdata); rdata += INT16SZ; + rdata += n; /*%< sig */ + n = ns_get16(rdata); rdata += INT16SZ; /*%< original id */ + sprintf(buf, "%d", ns_get16(rdata)); + rdata += INT16SZ; + addlen(strlen(buf), &buf, &buflen); + break; + } + + case ns_t_a6: { + struct in6_addr a; + int pbyte, pbit; + + /* prefix length */ + if (rdlen == 0U) goto formerr; + len = SPRINTF((tmp, "%d ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + pbit = *rdata; + if (pbit > 128) goto formerr; + pbyte = (pbit & ~7) / 8; + rdata++; + + /* address suffix: provided only when prefix len != 128 */ + if (pbit < 128) { + if (rdata + pbyte >= edata) goto formerr; + memset(&a, 0, sizeof(a)); + memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte); + (void) inet_ntop(AF_INET6, &a, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += sizeof(a) - pbyte; + } + + /* prefix name: provided only when prefix len > 0 */ + if (pbit == 0) + break; + if (rdata >= edata) goto formerr; + T(addstr(" ", 1, &buf, &buflen)); + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_opt: { + len = SPRINTF((tmp, "%u bytes", class)); + T(addstr(tmp, len, &buf, &buflen)); + break; + } + + case ns_t_ds: + case ns_t_dlv: + case ns_t_sshfp: { + u_int t; + + if (type == ns_t_ds || type == ns_t_dlv) { + if (rdlen < 4U) goto formerr; + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + } else + if (rdlen < 2U) goto formerr; + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + while (rdata < edata) { + len = SPRINTF((tmp, "%02X", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + } + break; + } + + case ns_t_nsec3: + case ns_t_nsec3param: { + u_int t, w, l, j, k, c; + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + t = *rdata++; + if (t == 0) { + T(addstr("-", 1, &buf, &buflen)); + } else { + while (t-- > 0) { + len = SPRINTF((tmp, "%02X", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + } + } + if (type == ns_t_nsec3param) + break; + T(addstr(" ", 1, &buf, &buflen)); + + t = *rdata++; + while (t > 0) { + switch (t) { + case 1: + tmp[0] = base32hex[((rdata[0]>>3)&0x1f)]; + tmp[1] = base32hex[((rdata[0]<<2)&0x1c)]; + tmp[2] = tmp[3] = tmp[4] = '='; + tmp[5] = tmp[6] = tmp[7] = '='; + break; + case 2: + tmp[0] = base32hex[((rdata[0]>>3)&0x1f)]; + tmp[1] = base32hex[((rdata[0]<<2)&0x1c)| + ((rdata[1]>>6)&0x03)]; + tmp[2] = base32hex[((rdata[1]>>1)&0x1f)]; + tmp[3] = base32hex[((rdata[1]<<4)&0x10)]; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = '='; + break; + case 3: + tmp[0] = base32hex[((rdata[0]>>3)&0x1f)]; + tmp[1] = base32hex[((rdata[0]<<2)&0x1c)| + ((rdata[1]>>6)&0x03)]; + tmp[2] = base32hex[((rdata[1]>>1)&0x1f)]; + tmp[3] = base32hex[((rdata[1]<<4)&0x10)| + ((rdata[2]>>4)&0x0f)]; + tmp[4] = base32hex[((rdata[2]<<1)&0x1e)]; + tmp[5] = tmp[6] = tmp[7] = '='; + break; + case 4: + tmp[0] = base32hex[((rdata[0]>>3)&0x1f)]; + tmp[1] = base32hex[((rdata[0]<<2)&0x1c)| + ((rdata[1]>>6)&0x03)]; + tmp[2] = base32hex[((rdata[1]>>1)&0x1f)]; + tmp[3] = base32hex[((rdata[1]<<4)&0x10)| + ((rdata[2]>>4)&0x0f)]; + tmp[4] = base32hex[((rdata[2]<<1)&0x1e)| + ((rdata[3]>>7)&0x01)]; + tmp[5] = base32hex[((rdata[3]>>2)&0x1f)]; + tmp[6] = base32hex[(rdata[3]<<3)&0x18]; + tmp[7] = '='; + break; + default: + tmp[0] = base32hex[((rdata[0]>>3)&0x1f)]; + tmp[1] = base32hex[((rdata[0]<<2)&0x1c)| + ((rdata[1]>>6)&0x03)]; + tmp[2] = base32hex[((rdata[1]>>1)&0x1f)]; + tmp[3] = base32hex[((rdata[1]<<4)&0x10)| + ((rdata[2]>>4)&0x0f)]; + tmp[4] = base32hex[((rdata[2]<<1)&0x1e)| + ((rdata[3]>>7)&0x01)]; + tmp[5] = base32hex[((rdata[3]>>2)&0x1f)]; + tmp[6] = base32hex[((rdata[3]<<3)&0x18)| + ((rdata[4]>>5)&0x07)]; + tmp[7] = base32hex[(rdata[4]&0x1f)]; + break; + } + T(addstr(tmp, 8, &buf, &buflen)); + if (t >= 5) { + rdata += 5; + t -= 5; + } else { + rdata += t; + t -= t; + } + } + + while (rdata < edata) { + w = *rdata++; + l = *rdata++; + for (j = 0; j < l; j++) { + if (rdata[j] == 0) + continue; + for (k = 0; k < 8; k++) { + if ((rdata[j] & (0x80 >> k)) == 0) + continue; + c = w * 256 + j * 8 + k; + len = SPRINTF((tmp, " %s", p_type(c))); + T(addstr(tmp, len, &buf, &buflen)); + } + } + rdata += l; + } + break; + } + + case ns_t_nsec: { + u_int w, l, j, k, c; + + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + while (rdata < edata) { + w = *rdata++; + l = *rdata++; + for (j = 0; j < l; j++) { + if (rdata[j] == 0) + continue; + for (k = 0; k < 8; k++) { + if ((rdata[j] & (0x80 >> k)) == 0) + continue; + c = w * 256 + j * 8 + k; + len = SPRINTF((tmp, " %s", p_type(c))); + T(addstr(tmp, len, &buf, &buflen)); + } + } + rdata += l; + } + break; + } + + case ns_t_dhcid: { + int n; + unsigned int siz; + char base64_dhcid[8192]; + const char *leader; + + siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ + if (siz > sizeof(base64_dhcid) * 3/4) { + const char *str = "record too long to print"; + T(addstr(str, strlen(str), &buf, &buflen)); + } else { + len = b64_ntop(rdata, edata-rdata, base64_dhcid, siz); + + if (len < 0) + goto formerr; + + else if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } + else + leader = " "; + + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), + &buf, &buflen)); + T(addstr(base64_dhcid + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + } + break; + } + + case ns_t_ipseckey: { + int n; + unsigned int siz; + char base64_key[8192]; + const char *leader; + + if (rdlen < 2) + goto formerr; + + switch (rdata[1]) { + case 0: + case 3: + if (rdlen < 3) + goto formerr; + break; + case 1: + if (rdlen < 7) + goto formerr; + break; + case 2: + if (rdlen < 19) + goto formerr; + break; + default: + comment = "unknown IPSECKEY gateway type"; + goto hexify; + } + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + len = SPRINTF((tmp, "%u ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + + switch (rdata[-2]) { + case 0: + T(addstr(".", 1, &buf, &buflen)); + break; + case 1: + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += 4; + break; + case 2: + (void) inet_ntop(AF_INET6, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += 16; + break; + case 3: + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + if (rdata >= edata) + break; + + siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ + if (siz > sizeof(base64_key) * 3/4) { + const char *str = "record too long to print"; + T(addstr(str, strlen(str), &buf, &buflen)); + } else { + len = b64_ntop(rdata, edata-rdata, base64_key, siz); + + if (len < 0) + goto formerr; + + else if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } + else + leader = " "; + + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), + &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + } + break; + } + + case ns_t_hip: { + unsigned int i, hip_len, algorithm, key_len; + char base64_key[NS_MD5RSA_MAX_BASE64]; + unsigned int siz; + const char *leader = "\n\t\t\t\t\t"; + + hip_len = *rdata++; + algorithm = *rdata++; + key_len = ns_get16(rdata); + rdata += NS_INT16SZ; + + siz = key_len*4/3 + 4; /* "+4" accounts for trailing \0 */ + if (siz > sizeof(base64_key) * 3/4) { + const char *str = "record too long to print"; + T(addstr(str, strlen(str), &buf, &buflen)); + } else { + len = sprintf(tmp, "( %u ", algorithm); + T(addstr(tmp, len, &buf, &buflen)); + + for (i = 0; i < hip_len; i++) { + len = sprintf(tmp, "%02X", *rdata); + T(addstr(tmp, len, &buf, &buflen)); + rdata++; + } + T(addstr(leader, strlen(leader), &buf, &buflen)); + + len = b64_ntop(rdata, key_len, base64_key, siz); + if (len < 0) + goto formerr; + + T(addstr(base64_key, len, &buf, &buflen)); + + rdata += key_len; + while (rdata < edata) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addname(msg, msglen, &rdata, origin, + &buf, &buflen)); + } + T(addstr(" )", 2, &buf, &buflen)); + } + break; + } + + default: + comment = "unknown RR type"; + goto hexify; + } + return (buf - obuf); + formerr: + comment = "RR format error"; + hexify: { + int n, m; + char *p; + + len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata), + rdlen != 0U ? " (" : "", comment)); + T(addstr(tmp, len, &buf, &buflen)); + while (rdata < edata) { + p = tmp; + p += SPRINTF((p, "\n\t")); + spaced = 0; + n = MIN(16, edata - rdata); + for (m = 0; m < n; m++) + p += SPRINTF((p, "%02x ", rdata[m])); + T(addstr(tmp, p - tmp, &buf, &buflen)); + if (n < 16) { + T(addstr(")", 1, &buf, &buflen)); + T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen)); + } + p = tmp; + p += SPRINTF((p, "; ")); + for (m = 0; m < n; m++) + *p++ = (isascii(rdata[m]) && isprint(rdata[m])) + ? rdata[m] + : '.'; + T(addstr(tmp, p - tmp, &buf, &buflen)); + rdata += n; + } + return (buf - obuf); + } +} + +/* Private. */ + +/*% + * size_t + * prune_origin(name, origin) + * Find out if the name is at or under the current origin. + * return: + * Number of characters in name before start of origin, + * or length of name if origin does not match. + * notes: + * This function should share code with samedomain(). + */ +static size_t +prune_origin(const char *name, const char *origin) { + const char *oname = name; + + while (*name != '\0') { + if (origin != NULL && ns_samename(name, origin) == 1) + return (name - oname - (name > oname)); + while (*name != '\0') { + if (*name == '\\') { + name++; + /* XXX need to handle \nnn form. */ + if (*name == '\0') + break; + } else if (*name == '.') { + name++; + break; + } + name++; + } + } + return (name - oname); +} + +/*% + * int + * charstr(rdata, edata, buf, buflen) + * Format a <character-string> into the presentation buffer. + * return: + * Number of rdata octets consumed + * 0 for protocol format error + * -1 for output buffer error + * side effects: + * buffer is advanced on success. + */ +static int +charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) { + const u_char *odata = rdata; + size_t save_buflen = *buflen; + char *save_buf = *buf; + + if (addstr("\"", 1, buf, buflen) < 0) + goto enospc; + if (rdata < edata) { + int n = *rdata; + + if (rdata + 1 + n <= edata) { + rdata++; + while (n-- > 0) { + if (strchr("\n\"\\", *rdata) != NULL) + if (addstr("\\", 1, buf, buflen) < 0) + goto enospc; + if (addstr((const char *)rdata, 1, + buf, buflen) < 0) + goto enospc; + rdata++; + } + } + } + if (addstr("\"", 1, buf, buflen) < 0) + goto enospc; + return (rdata - odata); + enospc: + errno = ENOSPC; + *buf = save_buf; + *buflen = save_buflen; + return (-1); +} + +static int +addname(const u_char *msg, size_t msglen, + const u_char **pp, const char *origin, + char **buf, size_t *buflen) +{ + size_t newlen, save_buflen = *buflen; + char *save_buf = *buf; + int n; + + n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen); + if (n < 0) + goto enospc; /*%< Guess. */ + newlen = prune_origin(*buf, origin); + if (**buf == '\0') { + goto root; + } else if (newlen == 0U) { + /* Use "@" instead of name. */ + if (newlen + 2 > *buflen) + goto enospc; /* No room for "@\0". */ + (*buf)[newlen++] = '@'; + (*buf)[newlen] = '\0'; + } else { + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') { + /* No trailing dot. */ + root: + if (newlen + 2 > *buflen) + goto enospc; /* No room for ".\0". */ + (*buf)[newlen++] = '.'; + (*buf)[newlen] = '\0'; + } + } + *pp += n; + addlen(newlen, buf, buflen); + **buf = '\0'; + return (newlen); + enospc: + errno = ENOSPC; + *buf = save_buf; + *buflen = save_buflen; + return (-1); +} + +static void +addlen(size_t len, char **buf, size_t *buflen) { + INSIST(len <= *buflen); + *buf += len; + *buflen -= len; +} + +static int +addstr(const char *src, size_t len, char **buf, size_t *buflen) { + if (len >= *buflen) { + errno = ENOSPC; + return (-1); + } + memcpy(*buf, src, len); + addlen(len, buf, buflen); + **buf = '\0'; + return (0); +} + +static int +addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) { + size_t save_buflen = *buflen; + char *save_buf = *buf; + int t; + + if (spaced || len >= target - 1) { + T(addstr(" ", 2, buf, buflen)); + spaced = 1; + } else { + for (t = (target - len - 1) / 8; t >= 0; t--) + if (addstr("\t", 1, buf, buflen) < 0) { + *buflen = save_buflen; + *buf = save_buf; + return (-1); + } + spaced = 0; + } + return (spaced); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c new file mode 100644 index 0000000000..76617de9ad --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c @@ -0,0 +1,296 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#if __OpenBSD__ +#include <sys/types.h> +#endif +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <resolv_joy.h> +#include <string.h> + +#include "port_after.h" + +#define CONSUME_SRC \ + do { \ + rdata += n, rdlen -= n; \ + } while (0) + +#define CONSUME_DST \ + do { \ + nrdata += n, nrdsiz -= n, nrdlen += n; \ + } while (0) + +#define UNPACK_DNAME \ + do { \ + size_t t; \ + \ + if ((n = ns_name_unpack2(msg,eom,rdata,nrdata,nrdsiz,&t))<0) {\ + errno = EMSGSIZE; \ + return (-1); \ + } \ + CONSUME_SRC; \ + n = t; \ + CONSUME_DST; \ + } while (0) + +#define UNPACK_SOME(x) \ + do { \ + n = (x); \ + if ((size_t)n > rdlen || (size_t)n > nrdsiz) { \ + errno = EMSGSIZE; \ + return (-1); \ + } \ + memcpy(nrdata, rdata, n); \ + CONSUME_SRC; CONSUME_DST; \ + } while (0) + +#define UNPACK_REST(x) \ + do { \ + n = (x); \ + if ((size_t)n != rdlen) { \ + errno = EMSGSIZE; \ + return (-1); \ + } \ + memcpy(nrdata, rdata, n); \ + CONSUME_SRC; CONSUME_DST; \ + } while (0) + +ssize_t +ns_rdata_unpack(const u_char *msg, const u_char *eom, + ns_type type, const u_char *rdata, size_t rdlen, + u_char *nrdata, size_t nrdsiz) +{ + size_t nrdlen = 0; + int n; + + switch (type) { + case ns_t_a: + UNPACK_REST(NS_INADDRSZ); + break; + case ns_t_aaaa: + UNPACK_REST(NS_IN6ADDRSZ); + break; + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ns: + case ns_t_ptr: + case ns_t_dname: + UNPACK_DNAME; + break; + case ns_t_soa: + UNPACK_DNAME; + UNPACK_DNAME; + UNPACK_SOME(NS_INT32SZ * 5); + break; + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: + UNPACK_SOME(NS_INT16SZ); + UNPACK_DNAME; + break; + case ns_t_px: + UNPACK_SOME(NS_INT16SZ); + UNPACK_DNAME; + UNPACK_DNAME; + break; + case ns_t_srv: + UNPACK_SOME(NS_INT16SZ * 3); + UNPACK_DNAME; + break; + case ns_t_minfo: + case ns_t_rp: + UNPACK_DNAME; + UNPACK_DNAME; + break; + default: + UNPACK_SOME(rdlen); + break; + } + if (rdlen > 0) { + errno = EMSGSIZE; + return (-1); + } + return (nrdlen); +} + +#define EQUAL_CONSUME \ + do { \ + rdata1 += n, rdlen1 -= n; \ + rdata2 += n, rdlen2 -= n; \ + } while (0) + +#define EQUAL_DNAME \ + do { \ + ssize_t n; \ + \ + if (rdlen1 != rdlen2) \ + return (0); \ + n = ns_name_eq(rdata1, rdlen1, rdata2, rdlen2); \ + if (n <= 0) \ + return (n); \ + n = rdlen1; \ + EQUAL_CONSUME; \ + } while (0) + +#define EQUAL_SOME(x) \ + do { \ + size_t n = (x); \ + \ + if (n > rdlen1 || n > rdlen2) { \ + errno = EMSGSIZE; \ + return (-1); \ + } \ + if (memcmp(rdata1, rdata2, n) != 0) \ + return (0); \ + EQUAL_CONSUME; \ + } while (0) + +int +ns_rdata_equal(ns_type type, + const u_char *rdata1, size_t rdlen1, + const u_char *rdata2, size_t rdlen2) +{ + switch (type) { + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ns: + case ns_t_ptr: + case ns_t_dname: + EQUAL_DNAME; + break; + case ns_t_soa: + /* "There can be only one." --Highlander */ + break; + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: + EQUAL_SOME(NS_INT16SZ); + EQUAL_DNAME; + break; + case ns_t_px: + EQUAL_SOME(NS_INT16SZ); + EQUAL_DNAME; + EQUAL_DNAME; + break; + case ns_t_srv: + EQUAL_SOME(NS_INT16SZ * 3); + EQUAL_DNAME; + break; + case ns_t_minfo: + case ns_t_rp: + EQUAL_DNAME; + EQUAL_DNAME; + break; + default: + EQUAL_SOME(rdlen1); + break; + } + if (rdlen1 != 0 || rdlen2 != 0) + return (0); + return (1); +} + +#define REFERS_DNAME \ + do { \ + int n; \ + \ + n = ns_name_eq(rdata, rdlen, nname, NS_MAXNNAME); \ + if (n < 0) \ + return (-1); \ + if (n > 0) \ + return (1); \ + n = dn_skipname(rdata, rdata + rdlen); \ + if (n < 0) \ + return (-1); \ + CONSUME_SRC; \ + } while (0) + +#define REFERS_SOME(x) \ + do { \ + size_t n = (x); \ + \ + if (n > rdlen) { \ + errno = EMSGSIZE; \ + return (-1); \ + } \ + CONSUME_SRC; \ + } while (0) + +int +ns_rdata_refers(ns_type type, + const u_char *rdata, size_t rdlen, + const u_char *nname) +{ + switch (type) { + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ns: + case ns_t_ptr: + case ns_t_dname: + REFERS_DNAME; + break; + case ns_t_soa: + REFERS_DNAME; + REFERS_DNAME; + REFERS_SOME(NS_INT32SZ * 5); + break; + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: + REFERS_SOME(NS_INT16SZ); + REFERS_DNAME; + break; + case ns_t_px: + REFERS_SOME(NS_INT16SZ); + REFERS_DNAME; + REFERS_DNAME; + break; + case ns_t_srv: + REFERS_SOME(NS_INT16SZ * 3); + REFERS_DNAME; + break; + case ns_t_minfo: + case ns_t_rp: + REFERS_DNAME; + REFERS_DNAME; + break; + default: + REFERS_SOME(rdlen); + break; + } + if (rdlen != 0) { + errno = EMSGSIZE; + return (-1); + } + return (0); +} diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c new file mode 100644 index 0000000000..b4c4ae68db --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <arpa/nameser.h> +#include <errno.h> +#include <string.h> + +#include "port_after.h" + +/*% + * Check whether a name belongs to a domain. + * + * Inputs: + *\li a - the domain whose ancestory is being verified + *\li b - the potential ancestor we're checking against + * + * Return: + *\li boolean - is a at or below b? + * + * Notes: + *\li Trailing dots are first removed from name and domain. + * Always compare complete subdomains, not only whether the + * domain name is the trailing string of the given name. + * + *\li "host.foobar.top" lies in "foobar.top" and in "top" and in "" + * but NOT in "bar.top" + */ + +int +ns_samedomain(const char *a, const char *b) { + size_t la, lb; + int diff, i, escaped; + const char *cp; + + la = strlen(a); + lb = strlen(b); + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ + if (la != 0U && a[la - 1] == '.') { + escaped = 0; + /* Note this loop doesn't get executed if la==1. */ + for (i = la - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + la--; + } + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ + if (lb != 0U && b[lb - 1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if lb==1 */ + for (i = lb - 2; i >= 0; i--) + if (b[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + lb--; + } + + /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ + if (lb == 0U) + return (1); + + /* 'b' longer than 'a' means 'a' can't be in 'b'. */ + if (lb > la) + return (0); + + /* 'a' and 'b' being equal at this point indicates sameness. */ + if (lb == la) + return (strncasecmp(a, b, lb) == 0); + + /* Ok, we know la > lb. */ + + diff = la - lb; + + /* + * If 'a' is only 1 character longer than 'b', then it can't be + * a subdomain of 'b' (because of the need for the '.' label + * separator). + */ + if (diff < 2) + return (0); + + /* + * If the character before the last 'lb' characters of 'b' + * isn't '.', then it can't be a match (this lets us avoid + * having "foobar.com" match "bar.com"). + */ + if (a[diff - 1] != '.') + return (0); + + /* + * We're not sure about that '.', however. It could be escaped + * and thus not a really a label separator. + */ + escaped = 0; + for (i = diff - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (escaped) + return (0); + + /* Now compare aligned trailing substring. */ + cp = a + diff; + return (strncasecmp(cp, b, lb) == 0); +} + +/*% + * is "a" a subdomain of "b"? + */ +int +ns_subdomain(const char *a, const char *b) { + return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); +} + +/*% + * make a canonical copy of domain name "src" + * + * notes: + * \code + * foo -> foo. + * foo. -> foo. + * foo.. -> foo. + * foo\. -> foo\.. + * foo\\. -> foo\\. + * \endcode + */ + +int +ns_makecanon(const char *src, char *dst, size_t dstsize) { + size_t n = strlen(src); + + if (n + sizeof "." > dstsize) { /*%< Note: sizeof == 2 */ + errno = EMSGSIZE; + return (-1); + } + strcpy(dst, src); + while (n >= 1U && dst[n - 1] == '.') /*%< Ends in "." */ + if (n >= 2U && dst[n - 2] == '\\' && /*%< Ends in "\." */ + (n < 3U || dst[n - 3] != '\\')) /*%< But not "\\." */ + break; + else + dst[--n] = '\0'; + dst[n++] = '.'; + dst[n] = '\0'; + return (0); +} + +/*% + * determine whether domain name "a" is the same as domain name "b" + * + * return: + *\li -1 on error + *\li 0 if names differ + *\li 1 if names are the same + */ + +int +ns_samename(const char *a, const char *b) { + char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; + + if (ns_makecanon(a, ta, sizeof ta) < 0 || + ns_makecanon(b, tb, sizeof tb) < 0) + return (-1); + if (strcasecmp(ta, tb) == 0) + return (1); + else + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c new file mode 100644 index 0000000000..4db8d33679 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <isc/dst.h> +#include <isc/assertions.h> + +#include "port_after.h" + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eob) { \ + errno = EMSGSIZE; \ + return(NS_TSIG_ERROR_NO_SPACE); \ + } \ + } while (0) + +/*% + * ns_sign + * + * Parameters: + *\li msg message to be sent + *\li msglen input - length of message + * output - length of signed message + *\li msgsize length of buffer containing message + *\li error value to put in the error field + *\li key tsig key used for signing + *\li querysig (response), the signature in the query + *\li querysiglen (response), the length of the signature in the query + *\li sig a buffer to hold the generated signature + *\li siglen input - length of signature buffer + * output - length of signature + * + * Errors: + *\li - bad input data (-1) + *\li - bad key / sign failed (-BADKEY) + *\li - not enough space (NS_TSIG_ERROR_NO_SPACE) + */ +int +ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k, + const u_char *querysig, int querysiglen, u_char *sig, int *siglen, + time_t in_timesigned) +{ + return(ns_sign2(msg, msglen, msgsize, error, k, + querysig, querysiglen, sig, siglen, + in_timesigned, NULL, NULL)); +} + +int +ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k, + const u_char *querysig, int querysiglen, u_char *sig, int *siglen, + time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr) +{ + HEADER *hp = (HEADER *)msg; + DST_KEY *key = (DST_KEY *)k; + u_char *cp, *eob; + u_char *lenp; + u_char *alg; + int n; + time_t timesigned; + u_char name[NS_MAXCDNAME]; + + dst_init(); + if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL) + return (-1); + + cp = msg + *msglen; + eob = msg + msgsize; + + /* Name. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + n = ns_name_pton(key->dk_key_name, name, sizeof name); + if (n != -1) + n = ns_name_pack(name, cp, eob - cp, + (const u_char **)dnptrs, + (const u_char **)lastdnptr); + + } else { + n = ns_name_pton("", name, sizeof name); + if (n != -1) + n = ns_name_pack(name, cp, eob - cp, NULL, NULL); + } + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + cp += n; + + /* Type, class, ttl, length (not filled in yet). */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(ns_t_tsig, cp); + PUTSHORT(ns_c_any, cp); + PUTLONG(0, cp); /*%< TTL */ + lenp = cp; + cp += 2; + + /* Alg. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + if (key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); + } + else + n = dn_comp("", cp, eob - cp, NULL, NULL); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + alg = cp; + cp += n; + + /* Time. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(0, cp); + timesigned = time(NULL); + if (error != ns_r_badtime) + PUTLONG(timesigned, cp); + else + PUTLONG(in_timesigned, cp); + PUTSHORT(NS_TSIG_FUDGE, cp); + + /* Compute the signature. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + void *ctx; + u_char buf[NS_MAXCDNAME], *cp2; + int n; + + dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); + + /* Digest the query signature, if this is a response. */ + if (querysiglen > 0 && querysig != NULL) { + u_int16_t len_n = htons(querysiglen); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + (u_char *)&len_n, INT16SZ, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + querysig, querysiglen, NULL, 0); + } + + /* Digest the message. */ + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen, + NULL, 0); + + /* Digest the key name. */ + n = ns_name_ntol(name, buf, sizeof(buf)); + INSIST(n > 0); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the class and TTL. */ + cp2 = buf; + PUTSHORT(ns_c_any, cp2); + PUTLONG(0, cp2); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, + NULL, 0); + + /* Digest the algorithm. */ + n = ns_name_ntol(alg, buf, sizeof(buf)); + INSIST(n > 0); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the time signed, fudge, error, and other data */ + cp2 = buf; + PUTSHORT(0, cp2); /*%< Top 16 bits of time */ + if (error != ns_r_badtime) + PUTLONG(timesigned, cp2); + else + PUTLONG(in_timesigned, cp2); + PUTSHORT(NS_TSIG_FUDGE, cp2); + PUTSHORT(error, cp2); /*%< Error */ + if (error != ns_r_badtime) + PUTSHORT(0, cp2); /*%< Other data length */ + else { + PUTSHORT(INT16SZ+INT32SZ, cp2); /*%< Other data length */ + PUTSHORT(0, cp2); /*%< Top 16 bits of time */ + PUTLONG(timesigned, cp2); + } + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, + NULL, 0); + + n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, + sig, *siglen); + if (n < 0) + return (-ns_r_badkey); + *siglen = n; + } else + *siglen = 0; + + /* Add the signature. */ + BOUNDS_CHECK(cp, INT16SZ + (*siglen)); + PUTSHORT(*siglen, cp); + memcpy(cp, sig, *siglen); + cp += (*siglen); + + /* The original message ID & error. */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ); + PUTSHORT(ntohs(hp->id), cp); /*%< already in network order */ + PUTSHORT(error, cp); + + /* Other data. */ + BOUNDS_CHECK(cp, INT16SZ); + if (error != ns_r_badtime) + PUTSHORT(0, cp); /*%< Other data length */ + else { + PUTSHORT(INT16SZ+INT32SZ, cp); /*%< Other data length */ + BOUNDS_CHECK(cp, INT32SZ+INT16SZ); + PUTSHORT(0, cp); /*%< Top 16 bits of time */ + PUTLONG(timesigned, cp); + } + + /* Go back and fill in the length. */ + PUTSHORT(cp - lenp - INT16SZ, lenp); + + hp->arcount = htons(ntohs(hp->arcount) + 1); + *msglen = (cp - msg); + return (0); +} + +int +ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen, + ns_tcp_tsig_state *state) +{ + dst_init(); + if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) + return (-1); + state->counter = -1; + state->key = k; + if (state->key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + if (querysiglen > (int)sizeof(state->sig)) + return (-1); + memcpy(state->sig, querysig, querysiglen); + state->siglen = querysiglen; + return (0); +} + +int +ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error, + ns_tcp_tsig_state *state, int done) +{ + return (ns_sign_tcp2(msg, msglen, msgsize, error, state, + done, NULL, NULL)); +} + +int +ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error, + ns_tcp_tsig_state *state, int done, + u_char **dnptrs, u_char **lastdnptr) +{ + u_char *cp, *eob, *lenp; + u_char buf[MAXDNAME], *cp2; + HEADER *hp = (HEADER *)msg; + time_t timesigned; + int n; + + if (msg == NULL || msglen == NULL || state == NULL) + return (-1); + + state->counter++; + if (state->counter == 0) + return (ns_sign2(msg, msglen, msgsize, error, state->key, + state->sig, state->siglen, + state->sig, &state->siglen, 0, + dnptrs, lastdnptr)); + + if (state->siglen > 0) { + u_int16_t siglen_n = htons(state->siglen); + dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx, + NULL, 0, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + (u_char *)&siglen_n, INT16SZ, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + state->sig, state->siglen, NULL, 0); + state->siglen = 0; + } + + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen, + NULL, 0); + + if (done == 0 && (state->counter % 100 != 0)) + return (0); + + cp = msg + *msglen; + eob = msg + msgsize; + + /* Name. */ + n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + cp += n; + + /* Type, class, ttl, length (not filled in yet). */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(ns_t_tsig, cp); + PUTSHORT(ns_c_any, cp); + PUTLONG(0, cp); /*%< TTL */ + lenp = cp; + cp += 2; + + /* Alg. */ + n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + cp += n; + + /* Time. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + PUTSHORT(0, cp); + timesigned = time(NULL); + PUTLONG(timesigned, cp); + PUTSHORT(NS_TSIG_FUDGE, cp); + + /* + * Compute the signature. + */ + + /* Digest the time signed and fudge. */ + cp2 = buf; + PUTSHORT(0, cp2); /*%< Top 16 bits of time */ + PUTLONG(timesigned, cp2); + PUTSHORT(NS_TSIG_FUDGE, cp2); + + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + buf, cp2 - buf, NULL, 0); + + n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, + state->sig, sizeof(state->sig)); + if (n < 0) + return (-ns_r_badkey); + state->siglen = n; + + /* Add the signature. */ + BOUNDS_CHECK(cp, INT16SZ + state->siglen); + PUTSHORT(state->siglen, cp); + memcpy(cp, state->sig, state->siglen); + cp += state->siglen; + + /* The original message ID & error. */ + BOUNDS_CHECK(cp, INT16SZ + INT16SZ); + PUTSHORT(ntohs(hp->id), cp); /*%< already in network order */ + PUTSHORT(error, cp); + + /* Other data. */ + BOUNDS_CHECK(cp, INT16SZ); + PUTSHORT(0, cp); + + /* Go back and fill in the length. */ + PUTSHORT(cp - lenp - INT16SZ, lenp); + + hp->arcount = htons(ntohs(hp->arcount) + 1); + *msglen = (cp - msg); + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c new file mode 100644 index 0000000000..635a11905e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * Copyright 2018 Joyent, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static int fmt1(int t, char s, char **buf, size_t *buflen); + +/* Macros. */ + +#define T(x) if ((x) < 0) return (-1); else (void)NULL + +/* Public. */ + +int +ns_format_ttl(u_long src, char *dst, size_t dstlen) { + char *odst = dst; + int secs, mins, hours, days, weeks, x; + char *p; + + secs = src % 60; src /= 60; + mins = src % 60; src /= 60; + hours = src % 24; src /= 24; + days = src % 7; src /= 7; + weeks = src; src = 0; + + x = 0; + if (weeks) { + T(fmt1(weeks, 'W', &dst, &dstlen)); + x++; + } + if (days) { + T(fmt1(days, 'D', &dst, &dstlen)); + x++; + } + if (hours) { + T(fmt1(hours, 'H', &dst, &dstlen)); + x++; + } + if (mins) { + T(fmt1(mins, 'M', &dst, &dstlen)); + x++; + } + if (secs || !(weeks || days || hours || mins)) { + T(fmt1(secs, 'S', &dst, &dstlen)); + x++; + } + + if (x > 1) { + int ch; + + for (p = odst; (ch = *p) != '\0'; p++) + if (isascii(ch) && isupper(ch)) + *p = tolower(ch); + } + + return (dst - odst); +} + +int +ns_parse_ttl(const char *src, u_long *dst) { + u_long ttl, tmp; + int ch, digits, dirty; + + ttl = 0; + tmp = 0; + digits = 0; + dirty = 0; + while ((ch = *src++) != '\0') { + if (!isascii(ch) || !isprint(ch)) + goto einval; + if (isdigit(ch)) { + tmp *= 10; + tmp += (ch - '0'); + digits++; + continue; + } + if (digits == 0) + goto einval; + if (islower(ch)) + ch = toupper(ch); + switch (ch) { + case 'W': tmp *= 7; + /* FALLTHROUGH */ + case 'D': tmp *= 24; + /* FALLTHROUGH */ + case 'H': tmp *= 60; + /* FALLTHROUGH */ + case 'M': tmp *= 60; + /* FALLTHROUGH */ + case 'S': break; + default: goto einval; + } + ttl += tmp; + tmp = 0; + digits = 0; + dirty = 1; + } + if (digits > 0) { + if (dirty) + goto einval; + else + ttl += tmp; + } else if (!dirty) + goto einval; + *dst = ttl; + return (0); + + einval: + errno = EINVAL; + return (-1); +} + +/* Private. */ + +static int +fmt1(int t, char s, char **buf, size_t *buflen) { + char tmp[50]; + size_t len; + + len = SPRINTF((tmp, "%d%c", t, s)); + if (len + 1 > *buflen) + return (-1); + strcpy(*buf, tmp); + *buf += len; + *buflen -= len; + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c new file mode 100644 index 0000000000..93d8d59865 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <isc/dst.h> + +#include "port_after.h" + +/* Private. */ + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + return (NS_TSIG_ERROR_FORMERR); \ + } \ + } while (0) + +/* Public. */ + +u_char * +ns_find_tsig(u_char *msg, u_char *eom) { + HEADER *hp = (HEADER *)msg; + int n, type; + u_char *cp = msg, *start; + + if (msg == NULL || eom == NULL || msg > eom) + return (NULL); + + if (cp + HFIXEDSZ >= eom) + return (NULL); + + if (hp->arcount == 0) + return (NULL); + + cp += HFIXEDSZ; + + n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount)); + if (n < 0) + return (NULL); + cp += n; + + n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount)); + if (n < 0) + return (NULL); + cp += n; + + n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount)); + if (n < 0) + return (NULL); + cp += n; + + n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1); + if (n < 0) + return (NULL); + cp += n; + + start = cp; + n = dn_skipname(cp, eom); + if (n < 0) + return (NULL); + cp += n; + if (cp + INT16SZ >= eom) + return (NULL); + + GETSHORT(type, cp); + if (type != ns_t_tsig) + return (NULL); + return (start); +} + +/* ns_verify + * + * Parameters: + *\li statp res stuff + *\li msg received message + *\li msglen length of message + *\li key tsig key used for verifying. + *\li querysig (response), the signature in the query + *\li querysiglen (response), the length of the signature in the query + *\li sig (query), a buffer to hold the signature + *\li siglen (query), input - length of signature buffer + * output - length of signature + * + * Errors: + *\li - bad input (-1) + *\li - invalid dns message (NS_TSIG_ERROR_FORMERR) + *\li - TSIG is not present (NS_TSIG_ERROR_NO_TSIG) + *\li - key doesn't match (-ns_r_badkey) + *\li - TSIG verification fails with BADKEY (-ns_r_badkey) + *\li - TSIG verification fails with BADSIG (-ns_r_badsig) + *\li - TSIG verification fails with BADTIME (-ns_r_badtime) + *\li - TSIG verification succeeds, error set to BAKEY (ns_r_badkey) + *\li - TSIG verification succeeds, error set to BADSIG (ns_r_badsig) + *\li - TSIG verification succeeds, error set to BADTIME (ns_r_badtime) + */ +int +ns_verify(u_char *msg, int *msglen, void *k, + const u_char *querysig, int querysiglen, u_char *sig, int *siglen, + time_t *timesigned, int nostrip) +{ + HEADER *hp = (HEADER *)msg; + DST_KEY *key = (DST_KEY *)k; + u_char *cp = msg, *eom; + char name[MAXDNAME], alg[MAXDNAME]; + u_char *recstart, *rdatastart; + u_char *sigstart, *otherstart; + int n; + int error; + u_int16_t type, length; + u_int16_t fudge, sigfieldlen, otherfieldlen; + + dst_init(); + if (msg == NULL || msglen == NULL || *msglen < 0) + return (-1); + + eom = msg + *msglen; + + recstart = ns_find_tsig(msg, eom); + if (recstart == NULL) + return (NS_TSIG_ERROR_NO_TSIG); + + cp = recstart; + + /* Read the key name. */ + n = dn_expand(msg, eom, cp, name, MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + cp += n; + + /* Read the type. */ + BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); + GETSHORT(type, cp); + if (type != ns_t_tsig) + return (NS_TSIG_ERROR_NO_TSIG); + + /* Skip the class and TTL, save the length. */ + cp += INT16SZ + INT32SZ; + GETSHORT(length, cp); + if (eom - cp != length) + return (NS_TSIG_ERROR_FORMERR); + + /* Read the algorithm name. */ + rdatastart = cp; + n = dn_expand(msg, eom, cp, alg, MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) + return (-ns_r_badkey); + cp += n; + + /* Read the time signed and fudge. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + cp += INT16SZ; + GETLONG((*timesigned), cp); + GETSHORT(fudge, cp); + + /* Read the signature. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(sigfieldlen, cp); + BOUNDS_CHECK(cp, sigfieldlen); + sigstart = cp; + cp += sigfieldlen; + + /* Skip id and read error. */ + BOUNDS_CHECK(cp, 2*INT16SZ); + cp += INT16SZ; + GETSHORT(error, cp); + + /* Parse the other data. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(otherfieldlen, cp); + BOUNDS_CHECK(cp, otherfieldlen); + otherstart = cp; + cp += otherfieldlen; + + if (cp != eom) + return (NS_TSIG_ERROR_FORMERR); + + /* Verify that the key used is OK. */ + if (key != NULL) { + if (key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + if (error != ns_r_badsig && error != ns_r_badkey) { + if (ns_samename(key->dk_key_name, name) != 1) + return (-ns_r_badkey); + } + } + + hp->arcount = htons(ntohs(hp->arcount) - 1); + + /* + * Do the verification. + */ + + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + void *ctx; + u_char buf[MAXDNAME]; + u_char buf2[MAXDNAME]; + + /* Digest the query signature, if this is a response. */ + dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); + if (querysiglen > 0 && querysig != NULL) { + u_int16_t len_n = htons(querysiglen); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + (u_char *)&len_n, INT16SZ, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + querysig, querysiglen, NULL, 0); + } + + /* Digest the message. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg, + NULL, 0); + + /* Digest the key name. */ + n = ns_name_pton(name, buf2, sizeof(buf2)); + if (n < 0) + return (-1); + n = ns_name_ntol(buf2, buf, sizeof(buf)); + if (n < 0) + return (-1); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the class and TTL. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + recstart + dn_skipname(recstart, eom) + INT16SZ, + INT16SZ + INT32SZ, NULL, 0); + + /* Digest the algorithm. */ + n = ns_name_pton(alg, buf2, sizeof(buf2)); + if (n < 0) + return (-1); + n = ns_name_ntol(buf2, buf, sizeof(buf)); + if (n < 0) + return (-1); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the time signed and fudge. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + rdatastart + dn_skipname(rdatastart, eom), + INT16SZ + INT32SZ + INT16SZ, NULL, 0); + + /* Digest the error and other data. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + otherstart - INT16SZ - INT16SZ, + otherfieldlen + INT16SZ + INT16SZ, NULL, 0); + + n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, + sigstart, sigfieldlen); + + if (n < 0) + return (-ns_r_badsig); + + if (sig != NULL && siglen != NULL) { + if (*siglen < sigfieldlen) + return (NS_TSIG_ERROR_NO_SPACE); + memcpy(sig, sigstart, sigfieldlen); + *siglen = sigfieldlen; + } + } else { + if (sigfieldlen > 0) + return (NS_TSIG_ERROR_FORMERR); + if (sig != NULL && siglen != NULL) + *siglen = 0; + } + + /* Reset the counter, since we still need to check for badtime. */ + hp->arcount = htons(ntohs(hp->arcount) + 1); + + /* Verify the time. */ + if (abs((*timesigned) - time(NULL)) > fudge) + return (-ns_r_badtime); + + if (nostrip == 0) { + *msglen = recstart - msg; + hp->arcount = htons(ntohs(hp->arcount) - 1); + } + + if (error != NOERROR) + return (error); + + return (0); +} + +int +ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen, + ns_tcp_tsig_state *state) +{ + dst_init(); + if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) + return (-1); + state->counter = -1; + state->key = k; + if (state->key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + if (querysiglen > (int)sizeof(state->sig)) + return (-1); + memcpy(state->sig, querysig, querysiglen); + state->siglen = querysiglen; + return (0); +} + +int +ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state, + int required) +{ + HEADER *hp = (HEADER *)msg; + u_char *recstart, *sigstart; + unsigned int sigfieldlen, otherfieldlen; + u_char *cp, *eom, *cp2; + char name[MAXDNAME], alg[MAXDNAME]; + u_char buf[MAXDNAME]; + int n, type, length, fudge, error; + time_t timesigned; + + if (msg == NULL || msglen == NULL || state == NULL) + return (-1); + + eom = msg + *msglen; + + state->counter++; + if (state->counter == 0) + return (ns_verify(msg, msglen, state->key, + state->sig, state->siglen, + state->sig, &state->siglen, ×igned, 0)); + + if (state->siglen > 0) { + u_int16_t siglen_n = htons(state->siglen); + + dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx, + NULL, 0, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + (u_char *)&siglen_n, INT16SZ, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + state->sig, state->siglen, NULL, 0); + state->siglen = 0; + } + + cp = recstart = ns_find_tsig(msg, eom); + + if (recstart == NULL) { + if (required) + return (NS_TSIG_ERROR_NO_TSIG); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + msg, *msglen, NULL, 0); + return (0); + } + + hp->arcount = htons(ntohs(hp->arcount) - 1); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + msg, recstart - msg, NULL, 0); + + /* Read the key name. */ + n = dn_expand(msg, eom, cp, name, MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + cp += n; + + /* Read the type. */ + BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); + GETSHORT(type, cp); + if (type != ns_t_tsig) + return (NS_TSIG_ERROR_NO_TSIG); + + /* Skip the class and TTL, save the length. */ + cp += INT16SZ + INT32SZ; + GETSHORT(length, cp); + if (eom - cp != length) + return (NS_TSIG_ERROR_FORMERR); + + /* Read the algorithm name. */ + n = dn_expand(msg, eom, cp, alg, MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) + return (-ns_r_badkey); + cp += n; + + /* Verify that the key used is OK. */ + if ((ns_samename(state->key->dk_key_name, name) != 1 || + state->key->dk_alg != KEY_HMAC_MD5)) + return (-ns_r_badkey); + + /* Read the time signed and fudge. */ + BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); + cp += INT16SZ; + GETLONG(timesigned, cp); + GETSHORT(fudge, cp); + + /* Read the signature. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(sigfieldlen, cp); + BOUNDS_CHECK(cp, sigfieldlen); + sigstart = cp; + cp += sigfieldlen; + + /* Skip id and read error. */ + BOUNDS_CHECK(cp, 2*INT16SZ); + cp += INT16SZ; + GETSHORT(error, cp); + + /* Parse the other data. */ + BOUNDS_CHECK(cp, INT16SZ); + GETSHORT(otherfieldlen, cp); + BOUNDS_CHECK(cp, otherfieldlen); + cp += otherfieldlen; + + if (cp != eom) + return (NS_TSIG_ERROR_FORMERR); + + /* + * Do the verification. + */ + + /* Digest the time signed and fudge. */ + cp2 = buf; + PUTSHORT(0, cp2); /*%< Top 16 bits of time. */ + PUTLONG(timesigned, cp2); + PUTSHORT(NS_TSIG_FUDGE, cp2); + + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + buf, cp2 - buf, NULL, 0); + + n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, + sigstart, sigfieldlen); + if (n < 0) + return (-ns_r_badsig); + + if (sigfieldlen > sizeof(state->sig)) + return (NS_TSIG_ERROR_NO_SPACE); + + memcpy(state->sig, sigstart, sigfieldlen); + state->siglen = sigfieldlen; + + /* Verify the time. */ + if (abs(timesigned - time(NULL)) > fudge) + return (-ns_r_badtime); + + *msglen = recstart - msg; + + if (error != NOERROR) + return (error); + + return (0); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/herror.c b/usr/src/lib/libresolv2_joy/common/resolv/herror.c new file mode 100644 index 0000000000..5181c5792c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/herror.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1987, 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. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> + +#include <netdb.h> +#include <resolv_joy.h> +#include <string.h> +#include <unistd.h> +#include <irs.h> + +#include "port_after.h" + +const char *h_errlist[] = { + "Resolver Error 0 (no error)", + "Unknown host", /*%< 1 HOST_NOT_FOUND */ + "Host name lookup failure", /*%< 2 TRY_AGAIN */ + "Unknown server error", /*%< 3 NO_RECOVERY */ + "No address associated with name", /*%< 4 NO_ADDRESS */ +}; +int h_nerr = { sizeof h_errlist / sizeof h_errlist[0] }; + +#if !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) +#undef h_errno +int h_errno; +#endif + +/*% + * herror -- + * print the error indicated by the h_errno value. + */ +void +herror(const char *s) { + struct iovec iov[4], *v = iov; + char *t; + + if (s != NULL && *s != '\0') { + DE_CONST(s, t); + v->iov_base = t; + v->iov_len = strlen(t); + v++; + DE_CONST(": ", t); + v->iov_base = t; + v->iov_len = 2; + v++; + } + DE_CONST(hstrerror(*__h_errno()), t); + v->iov_base = t; + v->iov_len = strlen(v->iov_base); + v++; + DE_CONST("\n", t); + v->iov_base = t; + v->iov_len = 1; + writev(STDERR_FILENO, iov, (v - iov) + 1); +} + +/*% + * hstrerror -- + * return the string associated with a given "host" errno value. + */ +const char * +hstrerror(int err) { + if (err < 0) + return ("Resolver internal error"); + else if (err < h_nerr) + return (h_errlist[err]); + return ("Unknown resolver error"); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c b/usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c new file mode 100644 index 0000000000..2e79b1e7b4 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c @@ -0,0 +1,135 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include <port_before.h> +#ifdef DO_PTHREADS +#include <pthread.h> +#endif +#include <errno.h> +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <resolv_mt.h> +#include <irs.h> +#include <port_after.h> + +#ifdef DO_PTHREADS +static pthread_key_t key; +static int mt_key_initialized = 0; + +static int __res_init_ctx(void); +static void __res_destroy_ctx(void *); + +#if defined(sun) && !defined(__GNUC__) +#pragma init (_mtctxres_init) +#endif +#endif + +static mtctxres_t sharedctx; + +#ifdef DO_PTHREADS +/* + * Initialize the TSD key. By doing this at library load time, we're + * implicitly running without interference from other threads, so there's + * no need for locking. + */ +static void +_mtctxres_init(void) { + int pthread_keycreate_ret; + + pthread_keycreate_ret = pthread_key_create(&key, __res_destroy_ctx); + if (pthread_keycreate_ret == 0) + mt_key_initialized = 1; +} +#endif + +/* + * To support binaries that used the private MT-safe interface in + * Solaris 8, we still need to provide the __res_enable_mt() + * and __res_disable_mt() entry points. They're do-nothing routines. + */ +int +__res_enable_mt(void) { + return (-1); +} + +int +__res_disable_mt(void) { + return (0); +} + +#ifdef DO_PTHREADS +static int +__res_init_ctx(void) { + + mtctxres_t *mt; + int ret; + + + if (pthread_getspecific(key) != 0) { + /* Already exists */ + return (0); + } + + if ((mt = malloc(sizeof (mtctxres_t))) == 0) { + errno = ENOMEM; + return (-1); + } + + memset(mt, 0, sizeof (mtctxres_t)); + + if ((ret = pthread_setspecific(key, mt)) != 0) { + free(mt); + errno = ret; + return (-1); + } + + return (0); +} + +static void +__res_destroy_ctx(void *value) { + + mtctxres_t *mt = (mtctxres_t *)value; + + if (mt != 0) + free(mt); +} +#endif + +mtctxres_t * +___mtctxres(void) { +#ifdef DO_PTHREADS + mtctxres_t *mt; + + /* + * This if clause should only be executed if we are linking + * statically. When linked dynamically _mtctxres_init() should + * be called at binding time due the #pragma above. + */ + if (!mt_key_initialized) { + static pthread_mutex_t keylock = PTHREAD_MUTEX_INITIALIZER; + if (pthread_mutex_lock(&keylock) == 0) { + _mtctxres_init(); + (void) pthread_mutex_unlock(&keylock); + } + } + + /* + * If we have already been called in this thread return the existing + * context. Otherwise recreat a new context and return it. If + * that fails return a global context. + */ + if (mt_key_initialized) { + if (((mt = pthread_getspecific(key)) != 0) || + (__res_init_ctx() == 0 && + (mt = pthread_getspecific(key)) != 0)) { + return (mt); + } + } +#endif + return (&sharedctx); +} diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c b/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c new file mode 100644 index 0000000000..abe9b55155 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c @@ -0,0 +1,282 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (c) 1985, 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 (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <ctype.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "port_after.h" + +#ifndef ORIGINAL_ISC_CODE +#pragma weak __dn_skipname = dn_skipname +#pragma weak __res_dnok = res_dnok +#pragma weak __res_hnok = res_hnok +#pragma weak __res_mailok = res_mailok +#pragma weak __res_ownok = res_ownok +#endif /* ORIGINAL_ISC_CODE */ + +/*% + * Expand compressed domain name 'src' to full domain name. + * + * \li 'msg' is a pointer to the begining of the message, + * \li 'eom' points to the first location after the message, + * \li 'dst' is a pointer to a buffer of size 'dstsiz' for the result. + * \li Return size of compressed name or -1 if there was an error. + */ +int +dn_expand(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, int dstsiz) +{ + int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); + + if (n > 0 && dst[0] == '.') + dst[0] = '\0'; + return (n); +} + +/*% + * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. + * + * \li Return the size of the compressed name or -1. + * \li 'length' is the size of the array pointed to by 'comp_dn'. + */ +int +dn_comp(const char *src, u_char *dst, int dstsiz, + u_char **dnptrs, u_char **lastdnptr) +{ + return (ns_name_compress(src, dst, (size_t)dstsiz, + (const u_char **)dnptrs, + (const u_char **)lastdnptr)); +} + + +/*% + * Skip over a compressed domain name. Return the size or -1. + */ +int +dn_skipname(const u_char *ptr, const u_char *eom) { + const u_char *saveptr = ptr; + + if (ns_name_skip(&ptr, eom) == -1) + return (-1); + return (ptr - saveptr); +} + +/*% + * Verify that a domain name uses an acceptable character set. + * + * Note the conspicuous absence of ctype macros in these definitions. On + * non-ASCII hosts, we can't depend on string literals or ctype macros to + * tell us anything about network-format data. The rest of the BIND system + * is not careful about this, but for some reason, we're doing it right here. + */ +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#ifdef SUNW_HNOK_UNDERSCORE +#define underscorechar(c) ((c) == 0x5f) +#endif /* SUNW_HNOK_UNDERSCORE */ +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ + || ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#ifdef SUNW_HNOK_UNDERSCORE +#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c)) +#else +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#endif /* SUNW_HNOK_UNDERSCORE */ +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +int +res_hnok(const char *dn) { + int pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + (void)NULL; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + pch = ch, ch = nch; + } + return (1); +} + +/*% + * hostname-like (A, MX, WKS) owners can have "*" as their first label + * but must otherwise be as a host name. + */ +int +res_ownok(const char *dn) { + if (asterchar(dn[0])) { + if (periodchar(dn[1])) + return (res_hnok(dn+2)); + if (dn[1] == '\0') + return (1); + } + return (res_hnok(dn)); +} + +/*% + * SOA RNAMEs and RP RNAMEs can have any printable character in their first + * label, but the rest of the name has to look like a host name. + */ +int +res_mailok(const char *dn) { + int ch, escaped = 0; + + /* "." is a valid missing representation */ + if (*dn == '\0') + return (1); + + /* otherwise <label>.<hostname> */ + while ((ch = *dn++) != '\0') { + if (!domainchar(ch)) + return (0); + if (!escaped && periodchar(ch)) + break; + if (escaped) + escaped = 0; + else if (bslashchar(ch)) + escaped = 1; + } + if (periodchar(ch)) + return (res_hnok(dn)); + return (0); +} + +/*% + * This function is quite liberal, since RFC1034's character sets are only + * recommendations. + */ +int +res_dnok(const char *dn) { + int ch; + + while ((ch = *dn++) != '\0') + if (!domainchar(ch)) + return (0); + return (1); +} + +#ifdef BIND_4_COMPAT +/*% + * This module must export the following externally-visible symbols: + * ___putlong + * ___putshort + * __getlong + * __getshort + * Note that one _ comes from C and the others come from us. + */ + +#ifdef SOLARIS2 +#ifdef __putlong +#undef __putlong +#endif +#ifdef __putshort +#undef __putshort +#endif +#pragma weak putlong = __putlong +#pragma weak putshort = __putshort +#endif /* SOLARIS2 */ + +void __putlong(u_int32_t src, u_char *dst) { ns_put32(src, dst); } +void __putshort(u_int16_t src, u_char *dst) { ns_put16(src, dst); } +#ifndef __ultrix__ +u_int32_t _getlong(const u_char *src) { return (ns_get32(src)); } +u_int16_t _getshort(const u_char *src) { return (ns_get16(src)); } +#endif /*__ultrix__*/ +#endif /*BIND_4_COMPAT*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_data.c b/usr/src/lib/libresolv2_joy/common/resolv/res_data.c new file mode 100644 index 0000000000..514a9e56a1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_data.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + */ + + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1995-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <res_update.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "port_after.h" + +#ifndef ORIGINAL_ISC_CODE +#pragma weak __fp_nquery = fp_nquery +#pragma weak __fp_query = fp_query +#pragma weak __p_query = p_query +#pragma weak __hostalias = hostalias +#pragma weak __res_randomid = res_randomid +#endif + +const char *_res_opcodes[] = { + "QUERY", + "IQUERY", + "CQUERYM", + "CQUERYU", /*%< experimental */ + "NOTIFY", /*%< experimental */ + "UPDATE", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "ZONEINIT", + "ZONEREF", +}; + +#ifdef BIND_UPDATE +const char *_res_sectioncodes[] = { + "ZONE", + "PREREQUISITES", + "UPDATE", + "ADDITIONAL", +}; +#endif + +#undef _res +#ifndef __BIND_NOSTATIC +struct __res_state _res +# if defined(__BIND_RES_TEXT) + = { RES_TIMEOUT, } /*%< Motorola, et al. */ +# endif + ; + +#ifdef ORIGINAL_ISC_CODE +#if defined(DO_PTHREADS) || defined(__linux) +#define _res (*__res_state()) +#endif +#endif + +/* Proto. */ + +int res_ourserver_p(const res_state, const struct sockaddr_in *); + +int +res_init(void) { + extern int __res_vinit(res_state, int); + + /* + * These three fields used to be statically initialized. This made + * it hard to use this code in a shared library. It is necessary, + * now that we're doing dynamic initialization here, that we preserve + * the old semantics: if an application modifies one of these three + * fields of _res before res_init() is called, res_init() will not + * alter them. Of course, if an application is setting them to + * _zero_ before calling res_init(), hoping to override what used + * to be the static default, we can't detect it and unexpected results + * will follow. Zero for any of these fields would make no sense, + * so one can safely assume that the applications were already getting + * unexpected results. + * + * _res.options is tricky since some apps were known to diddle the bits + * before res_init() was first called. We can't replicate that semantic + * with dynamic initialization (they may have turned bits off that are + * set in RES_DEFAULT). Our solution is to declare such applications + * "broken". They could fool us by setting RES_INIT but none do (yet). + */ + if (!_res.retrans) + _res.retrans = RES_TIMEOUT; + if (!_res.retry) + _res.retry = 4; + if (!(_res.options & RES_INIT)) + _res.options = RES_DEFAULT; + + /* + * This one used to initialize implicitly to zero, so unless the app + * has set it to something in particular, we can randomize it now. + */ + if (!_res.id) + _res.id = res_nrandomid(&_res); + + return (__res_vinit(&_res, 1)); +} + +void +p_query(const u_char *msg) { + fp_query(msg, stdout); +} + +void +fp_query(const u_char *msg, FILE *file) { + fp_nquery(msg, PACKETSZ, file); +} + +void +fp_nquery(const u_char *msg, int len, FILE *file) { + if ((_res.options & RES_INIT) == 0U && res_init() == -1) + return; + + res_pquery(&_res, msg, len, file); +} + +int +res_mkquery(int op, /*!< opcode of query */ + const char *dname, /*!< domain name */ + int class, int type, /*!< class and type of query */ + const u_char *data, /*!< resource record data */ + int datalen, /*!< length of data */ + const u_char *newrr_in, /*!< new rr for modify or append */ + u_char *buf, /*!< buffer to put query */ + int buflen) /*!< size of buffer */ +{ + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + return (res_nmkquery(&_res, op, dname, class, type, + data, datalen, + newrr_in, buf, buflen)); +} + +int +res_mkupdate(ns_updrec *rrecp_in, u_char *buf, int buflen) { + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + + return (res_nmkupdate(&_res, rrecp_in, buf, buflen)); +} + +int +res_query(const char *name, /*!< domain name */ + int class, int type, /*!< class and type of query */ + u_char *answer, /*!< buffer to put answer */ + int anslen) /*!< size of answer buffer */ +{ + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + return (res_nquery(&_res, name, class, type, answer, anslen)); +} + +void +res_send_setqhook(res_send_qhook hook) { + _res.qhook = hook; +} + +void +res_send_setrhook(res_send_rhook hook) { + _res.rhook = hook; +} + +int +res_isourserver(const struct sockaddr_in *inp) { + return (res_ourserver_p(&_res, inp)); +} + +int +res_send(const u_char *buf, int buflen, u_char *ans, int anssiz) { + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + /* errno should have been set by res_init() in this case. */ + return (-1); + } + + return (res_nsend(&_res, buf, buflen, ans, anssiz)); +} + +int +res_sendsigned(const u_char *buf, int buflen, ns_tsig_key *key, + u_char *ans, int anssiz) +{ + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + /* errno should have been set by res_init() in this case. */ + return (-1); + } + + return (res_nsendsigned(&_res, buf, buflen, key, ans, anssiz)); +} + +void +res_close(void) { + res_nclose(&_res); +} + +int +res_update(ns_updrec *rrecp_in) { + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + + return (res_nupdate(&_res, rrecp_in, NULL)); +} + +int +res_search(const char *name, /*!< domain name */ + int class, int type, /*!< class and type of query */ + u_char *answer, /*!< buffer to put answer */ + int anslen) /*!< size of answer */ +{ + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + + return (res_nsearch(&_res, name, class, type, answer, anslen)); +} + +int +res_querydomain(const char *name, + const char *domain, + int class, int type, /*!< class and type of query */ + u_char *answer, /*!< buffer to put answer */ + int anslen) /*!< size of answer */ +{ + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + + return (res_nquerydomain(&_res, name, domain, + class, type, + answer, anslen)); +} + +u_int +res_randomid(void) { + if ((_res.options & RES_INIT) == 0U && res_init() == -1) { + RES_SET_H_ERRNO(&_res, NETDB_INTERNAL); + return (-1); + } + + return (res_nrandomid(&_res)); +} + +const char * +hostalias(const char *name) { + static char abuf[MAXDNAME]; + + return (res_hostalias(&_res, name, abuf, sizeof abuf)); +} + +#ifdef ultrix +int +local_hostname_length(const char *hostname) { + int len_host, len_domain; + + if (!*_res.defdname) + res_init(); + len_host = strlen(hostname); + len_domain = strlen(_res.defdname); + if (len_host > len_domain && + !strcasecmp(hostname + len_host - len_domain, _res.defdname) && + hostname[len_host - len_domain - 1] == '.') + return (len_host - len_domain - 1); + return (0); +} +#endif /*ultrix*/ + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c new file mode 100644 index 0000000000..2fcc1ad2ca --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c @@ -0,0 +1,1247 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Portions Copyright (C) 2004, 2005, 2008, 2009 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1996-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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 (c) 1985 + * 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <resolv_mt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "port_after.h" + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) sprintf x +#endif + +extern const char *_res_opcodes[]; +extern const char *_res_sectioncodes[]; + +#ifndef ORIGINAL_ISC_CODE +#pragma weak __dn_count_labels = dn_count_labels +#pragma weak __fp_resstat = fp_resstat +#pragma weak __loc_aton = loc_aton +#pragma weak __loc_ntoa = loc_ntoa +#pragma weak __p_cdname = p_cdname +#pragma weak __p_class = p_class +#pragma weak __p_section = p_section +#pragma weak __p_time = p_time +#pragma weak __p_type = p_type +#pragma weak __sym_ntop = sym_ntop +#pragma weak __sym_ntos = sym_ntos +#pragma weak __sym_ston = sym_ston +#endif /* ORIGINAL_ISC_CODE */ + +/*% + * Print the current options. + */ +void +fp_resstat(const res_state statp, FILE *file) { + u_long mask; + + fprintf(file, ";; res options:"); + for (mask = 1; mask != 0U; mask <<= 1) + if (statp->options & mask) + fprintf(file, " %s", p_option(mask)); + putc('\n', file); +} + +static void +do_section(const res_state statp, + ns_msg *handle, ns_sect section, + int pflag, FILE *file) +{ + int n, sflag, rrnum; + static int buflen = 2048; + char *buf; + ns_opcode opcode; + ns_rr rr; + + /* + * Print answer records. + */ + sflag = (statp->pfcode & pflag); + if (statp->pfcode && !sflag) + return; + + buf = malloc(buflen); + if (buf == NULL) { + fprintf(file, ";; memory allocation failure\n"); + return; + } + + opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode); + rrnum = 0; + for (;;) { + if (ns_parserr(handle, section, rrnum, &rr)) { + if (errno != ENODEV) + fprintf(file, ";; ns_parserr: %s\n", + strerror(errno)); + else if (rrnum > 0 && sflag != 0 && + (statp->pfcode & RES_PRF_HEAD1)) + putc('\n', file); + goto cleanup; + } + if (rrnum == 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1)) + fprintf(file, ";; %s SECTION:\n", + p_section(section, opcode)); + if (section == ns_s_qd) + fprintf(file, ";;\t%s, type = %s, class = %s\n", + ns_rr_name(rr), + p_type(ns_rr_type(rr)), + p_class(ns_rr_class(rr))); + else if (section == ns_s_ar && ns_rr_type(rr) == ns_t_opt) { + u_int16_t optcode, optlen, rdatalen = ns_rr_rdlen(rr); + u_int32_t ttl = ns_rr_ttl(rr); + + fprintf(file, + "; EDNS: version: %u, udp=%u, flags=%04x\n", + (ttl>>16)&0xff, ns_rr_class(rr), ttl&0xffff); + + while (rdatalen >= 4) { + const u_char *cp = ns_rr_rdata(rr); + int i; + + GETSHORT(optcode, cp); + GETSHORT(optlen, cp); + + if (optcode == NS_OPT_NSID) { + fputs("; NSID: ", file); + if (optlen == 0) { + fputs("; NSID\n", file); + } else { + fputs("; NSID: ", file); + for (i = 0; i < optlen; i++) + fprintf(file, "%02x ", + cp[i]); + fputs(" (",file); + for (i = 0; i < optlen; i++) + fprintf(file, "%c", + isprint(cp[i])? + cp[i] : '.'); + fputs(")\n", file); + } + } else { + if (optlen == 0) { + fprintf(file, "; OPT=%u\n", + optcode); + } else { + fprintf(file, "; OPT=%u: ", + optcode); + for (i = 0; i < optlen; i++) + fprintf(file, "%02x ", + cp[i]); + fputs(" (",file); + for (i = 0; i < optlen; i++) + fprintf(file, "%c", + isprint(cp[i]) ? + cp[i] : '.'); + fputs(")\n", file); + } + } + rdatalen -= 4 + optlen; + } + } else { + n = ns_sprintrr(handle, &rr, NULL, NULL, + buf, buflen); + if (n < 0) { + if (errno == ENOSPC) { + free(buf); + buf = NULL; + if (buflen < 131072) + buf = malloc(buflen += 1024); + if (buf == NULL) { + fprintf(file, + ";; memory allocation failure\n"); + return; + } + continue; + } + fprintf(file, ";; ns_sprintrr: %s\n", + strerror(errno)); + goto cleanup; + } + fputs(buf, file); + fputc('\n', file); + } + rrnum++; + } + cleanup: + if (buf != NULL) + free(buf); +} + +/*% + * Print the contents of a query. + * This is intended to be primarily a debugging routine. + */ +void +res_pquery(const res_state statp, const u_char *msg, int len, FILE *file) { + ns_msg handle; + int qdcount, ancount, nscount, arcount; + u_int opcode, rcode, id; + + if (ns_initparse(msg, len, &handle) < 0) { + fprintf(file, ";; ns_initparse: %s\n", strerror(errno)); + return; + } + opcode = ns_msg_getflag(handle, ns_f_opcode); + rcode = ns_msg_getflag(handle, ns_f_rcode); + id = ns_msg_id(handle); + qdcount = ns_msg_count(handle, ns_s_qd); + ancount = ns_msg_count(handle, ns_s_an); + nscount = ns_msg_count(handle, ns_s_ns); + arcount = ns_msg_count(handle, ns_s_ar); + + /* + * Print header fields. + */ + if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX) || rcode) + fprintf(file, + ";; ->>HEADER<<- opcode: %s, status: %s, id: %d\n", + _res_opcodes[opcode], p_rcode(rcode), id); + if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX)) + putc(';', file); + if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD2)) { + fprintf(file, "; flags:"); + if (ns_msg_getflag(handle, ns_f_qr)) + fprintf(file, " qr"); + if (ns_msg_getflag(handle, ns_f_aa)) + fprintf(file, " aa"); + if (ns_msg_getflag(handle, ns_f_tc)) + fprintf(file, " tc"); + if (ns_msg_getflag(handle, ns_f_rd)) + fprintf(file, " rd"); + if (ns_msg_getflag(handle, ns_f_ra)) + fprintf(file, " ra"); + if (ns_msg_getflag(handle, ns_f_z)) + fprintf(file, " ??"); + if (ns_msg_getflag(handle, ns_f_ad)) + fprintf(file, " ad"); + if (ns_msg_getflag(handle, ns_f_cd)) + fprintf(file, " cd"); + } + if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD1)) { + fprintf(file, "; %s: %d", + p_section(ns_s_qd, opcode), qdcount); + fprintf(file, ", %s: %d", + p_section(ns_s_an, opcode), ancount); + fprintf(file, ", %s: %d", + p_section(ns_s_ns, opcode), nscount); + fprintf(file, ", %s: %d", + p_section(ns_s_ar, opcode), arcount); + } + if ((!statp->pfcode) || (statp->pfcode & + (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) { + putc('\n',file); + } + /* + * Print the various sections. + */ + do_section(statp, &handle, ns_s_qd, RES_PRF_QUES, file); + do_section(statp, &handle, ns_s_an, RES_PRF_ANS, file); + do_section(statp, &handle, ns_s_ns, RES_PRF_AUTH, file); + do_section(statp, &handle, ns_s_ar, RES_PRF_ADD, file); + if (qdcount == 0 && ancount == 0 && + nscount == 0 && arcount == 0) + putc('\n', file); +} + +const u_char * +p_cdnname(const u_char *cp, const u_char *msg, int len, FILE *file) { + char name[MAXDNAME]; + int n; + + if ((n = dn_expand(msg, msg + len, cp, name, sizeof name)) < 0) + return (NULL); + if (name[0] == '\0') + putc('.', file); + else + fputs(name, file); + return (cp + n); +} + +const u_char * +p_cdname(const u_char *cp, const u_char *msg, FILE *file) { + return (p_cdnname(cp, msg, PACKETSZ, file)); +} + +/*% + * Return a fully-qualified domain name from a compressed name (with + length supplied). */ + +const u_char * +p_fqnname(cp, msg, msglen, name, namelen) + const u_char *cp, *msg; + int msglen; + char *name; + int namelen; +{ + int n, newlen; + + if ((n = dn_expand(msg, cp + msglen, cp, name, namelen)) < 0) + return (NULL); + newlen = strlen(name); + if (newlen == 0 || name[newlen - 1] != '.') { + if (newlen + 1 >= namelen) /*%< Lack space for final dot */ + return (NULL); + else + strcpy(name + newlen, "."); + } + return (cp + n); +} + +/* XXX: the rest of these functions need to become length-limited, too. */ + +const u_char * +p_fqname(const u_char *cp, const u_char *msg, FILE *file) { + char name[MAXDNAME]; + const u_char *n; + + n = p_fqnname(cp, msg, MAXCDNAME, name, sizeof name); + if (n == NULL) + return (NULL); + fputs(name, file); + return (n); +} + +/*% + * Names of RR classes and qclasses. Classes and qclasses are the same, except + * that C_ANY is a qclass but not a class. (You can ask for records of class + * C_ANY, but you can't have any records of that class in the database.) + */ +const struct res_sym __p_class_syms[] = { + {C_IN, "IN", (char *)0}, + {C_CHAOS, "CH", (char *)0}, + {C_CHAOS, "CHAOS", (char *)0}, + {C_HS, "HS", (char *)0}, + {C_HS, "HESIOD", (char *)0}, + {C_ANY, "ANY", (char *)0}, + {C_NONE, "NONE", (char *)0}, + {C_IN, (char *)0, (char *)0} +}; + +/*% + * Names of message sections. + */ +const struct res_sym __p_default_section_syms[] = { + {ns_s_qd, "QUERY", (char *)0}, + {ns_s_an, "ANSWER", (char *)0}, + {ns_s_ns, "AUTHORITY", (char *)0}, + {ns_s_ar, "ADDITIONAL", (char *)0}, + {0, (char *)0, (char *)0} +}; + +const struct res_sym __p_update_section_syms[] = { + {S_ZONE, "ZONE", (char *)0}, + {S_PREREQ, "PREREQUISITE", (char *)0}, + {S_UPDATE, "UPDATE", (char *)0}, + {S_ADDT, "ADDITIONAL", (char *)0}, + {0, (char *)0, (char *)0} +}; + +const struct res_sym __p_key_syms[] = { + {NS_ALG_MD5RSA, "RSA", "RSA KEY with MD5 hash"}, + {NS_ALG_DH, "DH", "Diffie Hellman"}, + {NS_ALG_DSA, "DSA", "Digital Signature Algorithm"}, + {NS_ALG_EXPIRE_ONLY, "EXPIREONLY", "No algorithm"}, + {NS_ALG_PRIVATE_OID, "PRIVATE", "Algorithm obtained from OID"}, + {0, NULL, NULL} +}; + +const struct res_sym __p_cert_syms[] = { + {cert_t_pkix, "PKIX", "PKIX (X.509v3) Certificate"}, + {cert_t_spki, "SPKI", "SPKI certificate"}, + {cert_t_pgp, "PGP", "PGP certificate"}, + {cert_t_url, "URL", "URL Private"}, + {cert_t_oid, "OID", "OID Private"}, + {0, NULL, NULL} +}; + +/*% + * Names of RR types and qtypes. Types and qtypes are the same, except + * that T_ANY is a qtype but not a type. (You can ask for records of type + * T_ANY, but you can't have any records of that type in the database.) + */ +const struct res_sym __p_type_syms[] = { + {ns_t_a, "A", "address"}, + {ns_t_ns, "NS", "name server"}, + {ns_t_md, "MD", "mail destination (deprecated)"}, + {ns_t_mf, "MF", "mail forwarder (deprecated)"}, + {ns_t_cname, "CNAME", "canonical name"}, + {ns_t_soa, "SOA", "start of authority"}, + {ns_t_mb, "MB", "mailbox"}, + {ns_t_mg, "MG", "mail group member"}, + {ns_t_mr, "MR", "mail rename"}, + {ns_t_null, "NULL", "null"}, + {ns_t_wks, "WKS", "well-known service (deprecated)"}, + {ns_t_ptr, "PTR", "domain name pointer"}, + {ns_t_hinfo, "HINFO", "host information"}, + {ns_t_minfo, "MINFO", "mailbox information"}, + {ns_t_mx, "MX", "mail exchanger"}, + {ns_t_txt, "TXT", "text"}, + {ns_t_rp, "RP", "responsible person"}, + {ns_t_afsdb, "AFSDB", "DCE or AFS server"}, + {ns_t_x25, "X25", "X25 address"}, + {ns_t_isdn, "ISDN", "ISDN address"}, + {ns_t_rt, "RT", "router"}, + {ns_t_nsap, "NSAP", "nsap address"}, + {ns_t_nsap_ptr, "NSAP_PTR", "domain name pointer"}, + {ns_t_sig, "SIG", "signature"}, + {ns_t_key, "KEY", "key"}, + {ns_t_px, "PX", "mapping information"}, + {ns_t_gpos, "GPOS", "geographical position (withdrawn)"}, + {ns_t_aaaa, "AAAA", "IPv6 address"}, + {ns_t_loc, "LOC", "location"}, + {ns_t_nxt, "NXT", "next valid name (unimplemented)"}, + {ns_t_eid, "EID", "endpoint identifier (unimplemented)"}, + {ns_t_nimloc, "NIMLOC", "NIMROD locator (unimplemented)"}, + {ns_t_srv, "SRV", "server selection"}, + {ns_t_atma, "ATMA", "ATM address (unimplemented)"}, + {ns_t_naptr, "NAPTR", "naptr"}, + {ns_t_kx, "KX", "key exchange"}, + {ns_t_cert, "CERT", "certificate"}, + {ns_t_a6, "A", "IPv6 address (experminental)"}, + {ns_t_dname, "DNAME", "non-terminal redirection"}, + {ns_t_opt, "OPT", "opt"}, + {ns_t_apl, "apl", "apl"}, + {ns_t_ds, "DS", "delegation signer"}, + {ns_t_sshfp, "SSFP", "SSH fingerprint"}, + {ns_t_ipseckey, "IPSECKEY", "IPSEC key"}, + {ns_t_rrsig, "RRSIG", "rrsig"}, + {ns_t_nsec, "NSEC", "nsec"}, + {ns_t_dnskey, "DNSKEY", "DNS key"}, + {ns_t_dhcid, "DHCID", "dynamic host configuration identifier"}, + {ns_t_nsec3, "NSEC3", "nsec3"}, + {ns_t_nsec3param, "NSEC3PARAM", "NSEC3 parameters"}, + {ns_t_hip, "HIP", "host identity protocol"}, + {ns_t_spf, "SPF", "sender policy framework"}, + {ns_t_tkey, "TKEY", "tkey"}, + {ns_t_tsig, "TSIG", "transaction signature"}, + {ns_t_ixfr, "IXFR", "incremental zone transfer"}, + {ns_t_axfr, "AXFR", "zone transfer"}, + {ns_t_zxfr, "ZXFR", "compressed zone transfer"}, + {ns_t_mailb, "MAILB", "mailbox-related data (deprecated)"}, + {ns_t_maila, "MAILA", "mail agent (deprecated)"}, + {ns_t_naptr, "NAPTR", "URN Naming Authority"}, + {ns_t_kx, "KX", "Key Exchange"}, + {ns_t_cert, "CERT", "Certificate"}, + {ns_t_a6, "A6", "IPv6 Address"}, + {ns_t_dname, "DNAME", "dname"}, + {ns_t_sink, "SINK", "Kitchen Sink (experimental)"}, + {ns_t_opt, "OPT", "EDNS Options"}, + {ns_t_any, "ANY", "\"any\""}, + {ns_t_dlv, "DLV", "DNSSEC look-aside validation"}, + {0, NULL, NULL} +}; + +/*% + * Names of DNS rcodes. + */ +const struct res_sym __p_rcode_syms[] = { + {ns_r_noerror, "NOERROR", "no error"}, + {ns_r_formerr, "FORMERR", "format error"}, + {ns_r_servfail, "SERVFAIL", "server failed"}, + {ns_r_nxdomain, "NXDOMAIN", "no such domain name"}, + {ns_r_notimpl, "NOTIMP", "not implemented"}, + {ns_r_refused, "REFUSED", "refused"}, + {ns_r_yxdomain, "YXDOMAIN", "domain name exists"}, + {ns_r_yxrrset, "YXRRSET", "rrset exists"}, + {ns_r_nxrrset, "NXRRSET", "rrset doesn't exist"}, + {ns_r_notauth, "NOTAUTH", "not authoritative"}, + {ns_r_notzone, "NOTZONE", "Not in zone"}, + {ns_r_max, "", ""}, + {ns_r_badsig, "BADSIG", "bad signature"}, + {ns_r_badkey, "BADKEY", "bad key"}, + {ns_r_badtime, "BADTIME", "bad time"}, + {0, NULL, NULL} +}; + +int +sym_ston(const struct res_sym *syms, const char *name, int *success) { + for ((void)NULL; syms->name != 0; syms++) { + if (strcasecmp (name, syms->name) == 0) { + if (success) + *success = 1; + return (syms->number); + } + } + if (success) + *success = 0; + return (syms->number); /*%< The default value. */ +} + +const char * +sym_ntos(const struct res_sym *syms, int number, int *success) { + char *unname = sym_ntos_unname; + + for ((void)NULL; syms->name != 0; syms++) { + if (number == syms->number) { + if (success) + *success = 1; + return (syms->name); + } + } + + sprintf(unname, "%d", number); /*%< XXX nonreentrant */ + if (success) + *success = 0; + return (unname); +} + +const char * +sym_ntop(const struct res_sym *syms, int number, int *success) { + char *unname = sym_ntop_unname; + + for ((void)NULL; syms->name != 0; syms++) { + if (number == syms->number) { + if (success) + *success = 1; + return (syms->humanname); + } + } + sprintf(unname, "%d", number); /*%< XXX nonreentrant */ + if (success) + *success = 0; + return (unname); +} + +/*% + * Return a string for the type. + */ +const char * +p_type(int type) { + int success; + const char *result; + static char typebuf[20]; + + result = sym_ntos(__p_type_syms, type, &success); + if (success) + return (result); + if (type < 0 || type > 0xffff) + return ("BADTYPE"); + sprintf(typebuf, "TYPE%d", type); + return (typebuf); +} + +/*% + * Return a string for the type. + */ +const char * +p_section(int section, int opcode) { + const struct res_sym *symbols; + + switch (opcode) { + case ns_o_update: + symbols = __p_update_section_syms; + break; + default: + symbols = __p_default_section_syms; + break; + } + return (sym_ntos(symbols, section, (int *)0)); +} + +/*% + * Return a mnemonic for class. + */ +const char * +p_class(int class) { + int success; + const char *result; + static char classbuf[20]; + + result = sym_ntos(__p_class_syms, class, &success); + if (success) + return (result); + if (class < 0 || class > 0xffff) + return ("BADCLASS"); + sprintf(classbuf, "CLASS%d", class); + return (classbuf); +} + +/*% + * Return a mnemonic for an option + */ +const char * +p_option(u_long option) { + char *nbuf = p_option_nbuf; + + switch (option) { + case RES_INIT: return "init"; + case RES_DEBUG: return "debug"; + case RES_AAONLY: return "aaonly(unimpl)"; + case RES_USEVC: return "usevc"; + case RES_PRIMARY: return "primry(unimpl)"; + case RES_IGNTC: return "igntc"; + case RES_RECURSE: return "recurs"; + case RES_DEFNAMES: return "defnam"; + case RES_STAYOPEN: return "styopn"; + case RES_DNSRCH: return "dnsrch"; + case RES_INSECURE1: return "insecure1"; + case RES_INSECURE2: return "insecure2"; + case RES_NOALIASES: return "noaliases"; + case RES_USE_INET6: return "inet6"; +#ifdef RES_USE_EDNS0 /*%< KAME extension */ + case RES_USE_EDNS0: return "edns0"; + case RES_NSID: return "nsid"; +#endif +#ifdef RES_USE_DNAME + case RES_USE_DNAME: return "dname"; +#endif +#ifdef RES_USE_DNSSEC + case RES_USE_DNSSEC: return "dnssec"; +#endif +#ifdef RES_NOTLDQUERY + case RES_NOTLDQUERY: return "no-tld-query"; +#endif +#ifdef RES_NO_NIBBLE2 + case RES_NO_NIBBLE2: return "no-nibble2"; +#endif + /* XXX nonreentrant */ + default: sprintf(nbuf, "?0x%lx?", (u_long)option); + return (nbuf); + } +} + +/*% + * Return a mnemonic for a time to live. + */ +const char * +p_time(u_int32_t value) { + char *nbuf = p_time_nbuf; + + if (ns_format_ttl(value, nbuf, sizeof nbuf) < 0) + sprintf(nbuf, "%u", value); + return (nbuf); +} + +/*% + * Return a string for the rcode. + */ +const char * +p_rcode(int rcode) { + return (sym_ntos(__p_rcode_syms, rcode, (int *)0)); +} + +/*% + * Return a string for a res_sockaddr_union. + */ +const char * +p_sockun(union res_sockaddr_union u, char *buf, size_t size) { + char ret[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:123.123.123.123"]; + + switch (u.sin.sin_family) { + case AF_INET: + inet_ntop(AF_INET, &u.sin.sin_addr, ret, sizeof ret); + break; +#ifdef HAS_INET6_STRUCTS + case AF_INET6: + inet_ntop(AF_INET6, &u.sin6.sin6_addr, ret, sizeof ret); + break; +#endif + default: + sprintf(ret, "[af%d]", u.sin.sin_family); + break; + } + if (size > 0U) { + strncpy(buf, ret, size - 1); + buf[size - 1] = '0'; + } + return (buf); +} + +/*% + * routines to convert between on-the-wire RR format and zone file format. + * Does not contain conversion to/from decimal degrees; divide or multiply + * by 60*60*1000 for that. + */ + +static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000, + 1000000,10000000,100000000,1000000000}; + +/*% takes an XeY precision/size value, returns a string representation. */ +static const char * +precsize_ntoa(prec) + u_int8_t prec; +{ + char *retbuf = precsize_ntoa_retbuf; + unsigned long val; + int mantissa, exponent; + + mantissa = (int)((prec >> 4) & 0x0f) % 10; + exponent = (int)((prec >> 0) & 0x0f) % 10; + + val = mantissa * poweroften[exponent]; + + (void) sprintf(retbuf, "%lu.%.2lu", val/100, val%100); + return (retbuf); +} + +/*% converts ascii size/precision X * 10**Y(cm) to 0xXY. moves pointer. */ +static u_int8_t +precsize_aton(const char **strptr) { + unsigned int mval = 0, cmval = 0; + u_int8_t retval = 0; + const char *cp; + int exponent; + int mantissa; + + cp = *strptr; + + while (isdigit((unsigned char)*cp)) + mval = mval * 10 + (*cp++ - '0'); + + if (*cp == '.') { /*%< centimeters */ + cp++; + if (isdigit((unsigned char)*cp)) { + cmval = (*cp++ - '0') * 10; + if (isdigit((unsigned char)*cp)) { + cmval += (*cp++ - '0'); + } + } + } + cmval = (mval * 100) + cmval; + + for (exponent = 0; exponent < 9; exponent++) + if (cmval < poweroften[exponent+1]) + break; + + mantissa = cmval / poweroften[exponent]; + if (mantissa > 9) + mantissa = 9; + + retval = (mantissa << 4) | exponent; + + *strptr = cp; + + return (retval); +} + +/*% converts ascii lat/lon to unsigned encoded 32-bit number. moves pointer. */ +static u_int32_t +latlon2ul(const char **latlonstrptr, int *which) { + const char *cp; + u_int32_t retval; + int deg = 0, min = 0, secs = 0, secsfrac = 0; + + cp = *latlonstrptr; + + while (isdigit((unsigned char)*cp)) + deg = deg * 10 + (*cp++ - '0'); + + while (isspace((unsigned char)*cp)) + cp++; + + if (!(isdigit((unsigned char)*cp))) + goto fndhemi; + + while (isdigit((unsigned char)*cp)) + min = min * 10 + (*cp++ - '0'); + + while (isspace((unsigned char)*cp)) + cp++; + + if (!(isdigit((unsigned char)*cp))) + goto fndhemi; + + while (isdigit((unsigned char)*cp)) + secs = secs * 10 + (*cp++ - '0'); + + if (*cp == '.') { /*%< decimal seconds */ + cp++; + if (isdigit((unsigned char)*cp)) { + secsfrac = (*cp++ - '0') * 100; + if (isdigit((unsigned char)*cp)) { + secsfrac += (*cp++ - '0') * 10; + if (isdigit((unsigned char)*cp)) { + secsfrac += (*cp++ - '0'); + } + } + } + } + + while (!isspace((unsigned char)*cp)) /*%< if any trailing garbage */ + cp++; + + while (isspace((unsigned char)*cp)) + cp++; + + fndhemi: + switch (*cp) { + case 'N': case 'n': + case 'E': case 'e': + retval = ((unsigned)1<<31) + + (((((deg * 60) + min) * 60) + secs) * 1000) + + secsfrac; + break; + case 'S': case 's': + case 'W': case 'w': + retval = ((unsigned)1<<31) + - (((((deg * 60) + min) * 60) + secs) * 1000) + - secsfrac; + break; + default: + retval = 0; /*%< invalid value -- indicates error */ + break; + } + + switch (*cp) { + case 'N': case 'n': + case 'S': case 's': + *which = 1; /*%< latitude */ + break; + case 'E': case 'e': + case 'W': case 'w': + *which = 2; /*%< longitude */ + break; + default: + *which = 0; /*%< error */ + break; + } + + cp++; /*%< skip the hemisphere */ + while (!isspace((unsigned char)*cp)) /*%< if any trailing garbage */ + cp++; + + while (isspace((unsigned char)*cp)) /*%< move to next field */ + cp++; + + *latlonstrptr = cp; + + return (retval); +} + +/*% + * converts a zone file representation in a string to an RDATA on-the-wire + * representation. */ +int +loc_aton(ascii, binary) + const char *ascii; + u_char *binary; +{ + const char *cp, *maxcp; + u_char *bcp; + + u_int32_t latit = 0, longit = 0, alt = 0; + u_int32_t lltemp1 = 0, lltemp2 = 0; + int altmeters = 0, altfrac = 0, altsign = 1; + u_int8_t hp = 0x16; /*%< default = 1e6 cm = 10000.00m = 10km */ + u_int8_t vp = 0x13; /*%< default = 1e3 cm = 10.00m */ + u_int8_t siz = 0x12; /*%< default = 1e2 cm = 1.00m */ + int which1 = 0, which2 = 0; + + cp = ascii; + maxcp = cp + strlen(ascii); + + lltemp1 = latlon2ul(&cp, &which1); + + lltemp2 = latlon2ul(&cp, &which2); + + switch (which1 + which2) { + case 3: /*%< 1 + 2, the only valid combination */ + if ((which1 == 1) && (which2 == 2)) { /*%< normal case */ + latit = lltemp1; + longit = lltemp2; + } else if ((which1 == 2) && (which2 == 1)) { /*%< reversed */ + longit = lltemp1; + latit = lltemp2; + } else { /*%< some kind of brokenness */ + return (0); + } + break; + default: /*%< we didn't get one of each */ + return (0); + } + + /* altitude */ + if (*cp == '-') { + altsign = -1; + cp++; + } + + if (*cp == '+') + cp++; + + while (isdigit((unsigned char)*cp)) + altmeters = altmeters * 10 + (*cp++ - '0'); + + if (*cp == '.') { /*%< decimal meters */ + cp++; + if (isdigit((unsigned char)*cp)) { + altfrac = (*cp++ - '0') * 10; + if (isdigit((unsigned char)*cp)) { + altfrac += (*cp++ - '0'); + } + } + } + + alt = (10000000 + (altsign * (altmeters * 100 + altfrac))); + + while (!isspace((unsigned char)*cp) && (cp < maxcp)) /*%< if trailing garbage or m */ + cp++; + + while (isspace((unsigned char)*cp) && (cp < maxcp)) + cp++; + + if (cp >= maxcp) + goto defaults; + + siz = precsize_aton(&cp); + + while (!isspace((unsigned char)*cp) && (cp < maxcp)) /*%< if trailing garbage or m */ + cp++; + + while (isspace((unsigned char)*cp) && (cp < maxcp)) + cp++; + + if (cp >= maxcp) + goto defaults; + + hp = precsize_aton(&cp); + + while (!isspace((unsigned char)*cp) && (cp < maxcp)) /*%< if trailing garbage or m */ + cp++; + + while (isspace((unsigned char)*cp) && (cp < maxcp)) + cp++; + + if (cp >= maxcp) + goto defaults; + + vp = precsize_aton(&cp); + + defaults: + + bcp = binary; + *bcp++ = (u_int8_t) 0; /*%< version byte */ + *bcp++ = siz; + *bcp++ = hp; + *bcp++ = vp; + PUTLONG(latit,bcp); + PUTLONG(longit,bcp); + PUTLONG(alt,bcp); + + return (16); /*%< size of RR in octets */ +} + +/*% takes an on-the-wire LOC RR and formats it in a human readable format. */ +const char * +loc_ntoa(binary, ascii) + const u_char *binary; + char *ascii; +{ + static const char *error = "?"; + static char tmpbuf[sizeof +"1000 60 60.000 N 1000 60 60.000 W -12345678.00m 90000000.00m 90000000.00m 90000000.00m"]; + const u_char *cp = binary; + + int latdeg, latmin, latsec, latsecfrac; + int longdeg, longmin, longsec, longsecfrac; + char northsouth, eastwest; + const char *altsign; + int altmeters, altfrac; + + const u_int32_t referencealt = 100000 * 100; + + int32_t latval, longval, altval; + u_int32_t templ; + u_int8_t sizeval, hpval, vpval, versionval; + + char *sizestr, *hpstr, *vpstr; + + versionval = *cp++; + + if (ascii == NULL) + ascii = tmpbuf; + + if (versionval) { + (void) sprintf(ascii, "; error: unknown LOC RR version"); + return (ascii); + } + + sizeval = *cp++; + + hpval = *cp++; + vpval = *cp++; + + GETLONG(templ, cp); + latval = (templ - ((unsigned)1<<31)); + + GETLONG(templ, cp); + longval = (templ - ((unsigned)1<<31)); + + GETLONG(templ, cp); + if (templ < referencealt) { /*%< below WGS 84 spheroid */ + altval = referencealt - templ; + altsign = "-"; + } else { + altval = templ - referencealt; + altsign = ""; + } + + if (latval < 0) { + northsouth = 'S'; + latval = -latval; + } else + northsouth = 'N'; + + latsecfrac = latval % 1000; + latval = latval / 1000; + latsec = latval % 60; + latval = latval / 60; + latmin = latval % 60; + latval = latval / 60; + latdeg = latval; + + if (longval < 0) { + eastwest = 'W'; + longval = -longval; + } else + eastwest = 'E'; + + longsecfrac = longval % 1000; + longval = longval / 1000; + longsec = longval % 60; + longval = longval / 60; + longmin = longval % 60; + longval = longval / 60; + longdeg = longval; + + altfrac = altval % 100; + altmeters = (altval / 100); + + sizestr = strdup(precsize_ntoa(sizeval)); + hpstr = strdup(precsize_ntoa(hpval)); + vpstr = strdup(precsize_ntoa(vpval)); + + sprintf(ascii, + "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %s%d.%.2dm %sm %sm %sm", + latdeg, latmin, latsec, latsecfrac, northsouth, + longdeg, longmin, longsec, longsecfrac, eastwest, + altsign, altmeters, altfrac, + (sizestr != NULL) ? sizestr : error, + (hpstr != NULL) ? hpstr : error, + (vpstr != NULL) ? vpstr : error); + + if (sizestr != NULL) + free(sizestr); + if (hpstr != NULL) + free(hpstr); + if (vpstr != NULL) + free(vpstr); + + return (ascii); +} + + +/*% Return the number of DNS hierarchy levels in the name. */ +int +dn_count_labels(const char *name) { + int i, len, count; + + len = strlen(name); + for (i = 0, count = 0; i < len; i++) { + /* XXX need to check for \. or use named's nlabels(). */ + if (name[i] == '.') + count++; + } + + /* don't count initial wildcard */ + if (name[0] == '*') + if (count) + count--; + + /* don't count the null label for root. */ + /* if terminating '.' not found, must adjust */ + /* count to include last label */ + if (len > 0 && name[len-1] != '.') + count++; + return (count); +} + +/*% + * Make dates expressed in seconds-since-Jan-1-1970 easy to read. + * SIG records are required to be printed like this, by the Secure DNS RFC. + */ +char * +p_secstodate (u_long secs) { + char *output = p_secstodate_output; + time_t clock = secs; + struct tm *time; +#ifdef HAVE_TIME_R + struct tm res; + + time = gmtime_r(&clock, &res); +#else + time = gmtime(&clock); +#endif + time->tm_year += 1900; + time->tm_mon += 1; + sprintf(output, "%04d%02d%02d%02d%02d%02d", + time->tm_year, time->tm_mon, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + return (output); +} + +u_int16_t +res_nametoclass(const char *buf, int *successp) { + unsigned long result; + char *endptr; + int success; + + result = sym_ston(__p_class_syms, buf, &success); + if (success) + goto done; + + if (strncasecmp(buf, "CLASS", 5) != 0 || + !isdigit((unsigned char)buf[5])) + goto done; + errno = 0; + result = strtoul(buf + 5, &endptr, 10); + if (errno == 0 && *endptr == '\0' && result <= 0xffffU) + success = 1; + done: + if (successp) + *successp = success; + return (result); +} + +u_int16_t +res_nametotype(const char *buf, int *successp) { + unsigned long result; + char *endptr; + int success; + + result = sym_ston(__p_type_syms, buf, &success); + if (success) + goto done; + + if (strncasecmp(buf, "type", 4) != 0 || + !isdigit((unsigned char)buf[4])) + goto done; + errno = 0; + result = strtoul(buf + 4, &endptr, 10); + if (errno == 0 && *endptr == '\0' && result <= 0xffffU) + success = 1; + done: + if (successp) + *successp = success; + return (result); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_debug.h b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.h new file mode 100644 index 0000000000..c28171d7c8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef _RES_DEBUG_H_ +#define _RES_DEBUG_H_ + +#ifndef DEBUG +# define Dprint(cond, args) /*empty*/ +# define DprintQ(cond, args, query, size) /*empty*/ +# define Aerror(statp, file, string, error, address) /*empty*/ +# define Perror(statp, file, string, error) /*empty*/ +#else +# define Dprint(cond, args) if (cond) {fprintf args;} else {} +# define DprintQ(cond, args, query, size) if (cond) {\ + fprintf args;\ + res_pquery(statp, query, size, stdout);\ + } else {} +#endif + +#endif /* _RES_DEBUG_H_ */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c b/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c new file mode 100644 index 0000000000..c84bd0dec7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* Import. */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/list.h> + +#include "port_after.h" + +#include <resolv_joy.h> + +/* Data structures. */ + +typedef struct rr_a { + LINK(struct rr_a) link; + union res_sockaddr_union addr; +} rr_a; +typedef LIST(rr_a) rrset_a; + +typedef struct rr_ns { + LINK(struct rr_ns) link; + const char * name; + unsigned int flags; + rrset_a addrs; +} rr_ns; +typedef LIST(rr_ns) rrset_ns; + +#define RR_NS_HAVE_V4 0x01 +#define RR_NS_HAVE_V6 0x02 + +/* Forward. */ + +static int satisfy(res_state, const char *, rrset_ns *, + union res_sockaddr_union *, int); +static int add_addrs(res_state, rr_ns *, + union res_sockaddr_union *, int); +static int get_soa(res_state, const char *, ns_class, int, + char *, size_t, char *, size_t, + rrset_ns *); +static int get_ns(res_state, const char *, ns_class, int, rrset_ns *); +static int get_glue(res_state, ns_class, int, rrset_ns *); +static int save_ns(res_state, ns_msg *, ns_sect, + const char *, ns_class, int, rrset_ns *); +static int save_a(res_state, ns_msg *, ns_sect, + const char *, ns_class, int, rr_ns *); +static void free_nsrrset(rrset_ns *); +static void free_nsrr(rrset_ns *, rr_ns *); +static rr_ns * find_ns(rrset_ns *, const char *); +static int do_query(res_state, const char *, ns_class, ns_type, + u_char *, ns_msg *); +static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2); + +/* Macros. */ + +#define DPRINTF(x) do {\ + int save_errno = errno; \ + if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \ + errno = save_errno; \ + } while (0) + +/* Public. */ + +/*% + * find enclosing zone for a <dname,class>, and some server addresses + * + * parameters: + *\li res - resolver context to work within (is modified) + *\li dname - domain name whose enclosing zone is desired + *\li class - class of dname (and its enclosing zone) + *\li zname - found zone name + *\li zsize - allocated size of zname + *\li addrs - found server addresses + *\li naddrs - max number of addrs + * + * return values: + *\li < 0 - an error occurred (check errno) + *\li = 0 - zname is now valid, but addrs[] wasn't changed + *\li > 0 - zname is now valid, and return value is number of addrs[] found + * + * notes: + *\li this function calls res_nsend() which means it depends on correctly + * functioning recursive nameservers (usually defined in /etc/resolv.conf + * or its local equivilent). + * + *\li we start by asking for an SOA<dname,class>. if we get one as an + * answer, that just means <dname,class> is a zone top, which is fine. + * more than likely we'll be told to go pound sand, in the form of a + * negative answer. + * + *\li note that we are not prepared to deal with referrals since that would + * only come from authority servers and our correctly functioning local + * recursive server would have followed the referral and got us something + * more definite. + * + *\li if the authority section contains an SOA, this SOA should also be the + * closest enclosing zone, since any intermediary zone cuts would've been + * returned as referrals and dealt with by our correctly functioning local + * recursive name server. but an SOA in the authority section should NOT + * match our dname (since that would have been returned in the answer + * section). an authority section SOA has to be "above" our dname. + * + *\li however, since authority section SOA's were once optional, it's + * possible that we'll have to go hunting for the enclosing SOA by + * ripping labels off the front of our dname -- this is known as "doing + * it the hard way." + * + *\li ultimately we want some server addresses, which are ideally the ones + * pertaining to the SOA.MNAME, but only if there is a matching NS RR. + * so the second phase (after we find an SOA) is to go looking for the + * NS RRset for that SOA's zone. + * + *\li no answer section processed by this code is allowed to contain CNAME + * or DNAME RR's. for the SOA query this means we strip a label and + * keep going. for the NS and A queries this means we just give up. + */ + +int +res_findzonecut(res_state statp, const char *dname, ns_class class, int opts, + char *zname, size_t zsize, struct in_addr *addrs, int naddrs) +{ + int result, i; + union res_sockaddr_union *u; + + + opts |= RES_IPV4ONLY; + opts &= ~RES_IPV6ONLY; + + u = calloc(naddrs, sizeof(*u)); + if (u == NULL) + return(-1); + + result = res_findzonecut2(statp, dname, class, opts, zname, zsize, + u, naddrs); + + for (i = 0; i < result; i++) { + addrs[i] = u[i].sin.sin_addr; + } + free(u); + return (result); +} + +int +res_findzonecut2(res_state statp, const char *dname, ns_class class, int opts, + char *zname, size_t zsize, union res_sockaddr_union *addrs, + int naddrs) +{ + char mname[NS_MAXDNAME]; + u_long save_pfcode; + rrset_ns nsrrs; + int n; + + DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d", + dname, p_class(class), (long)zsize, naddrs)); + save_pfcode = statp->pfcode; + statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX | + RES_PRF_QUES | RES_PRF_ANS | + RES_PRF_AUTH | RES_PRF_ADD; + INIT_LIST(nsrrs); + + DPRINTF(("get the soa, and see if it has enough glue")); + if ((n = get_soa(statp, dname, class, opts, zname, zsize, + mname, sizeof mname, &nsrrs)) < 0 || + ((opts & RES_EXHAUSTIVE) == 0 && + (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) + goto done; + + DPRINTF(("get the ns rrset and see if it has enough glue")); + if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 || + ((opts & RES_EXHAUSTIVE) == 0 && + (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0)) + goto done; + + DPRINTF(("get the missing glue and see if it's finally enough")); + if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0) + n = satisfy(statp, mname, &nsrrs, addrs, naddrs); + + done: + DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK")); + free_nsrrset(&nsrrs); + statp->pfcode = save_pfcode; + return (n); +} + +/* Private. */ + +static int +satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp, + union res_sockaddr_union *addrs, int naddrs) +{ + rr_ns *nsrr; + int n, x; + + n = 0; + nsrr = find_ns(nsrrsp, mname); + if (nsrr != NULL) { + x = add_addrs(statp, nsrr, addrs, naddrs); + addrs += x; + naddrs -= x; + n += x; + } + for (nsrr = HEAD(*nsrrsp); + nsrr != NULL && naddrs > 0; + nsrr = NEXT(nsrr, link)) + if (ns_samename(nsrr->name, mname) != 1) { + x = add_addrs(statp, nsrr, addrs, naddrs); + addrs += x; + naddrs -= x; + n += x; + } + DPRINTF(("satisfy(%s): %d", mname, n)); + return (n); +} + +static int +add_addrs(res_state statp, rr_ns *nsrr, + union res_sockaddr_union *addrs, int naddrs) +{ + rr_a *arr; + int n = 0; + + for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) { + if (naddrs <= 0) + return (0); + *addrs++ = arr->addr; + naddrs--; + n++; + } + DPRINTF(("add_addrs: %d", n)); + return (n); +} + +static int +get_soa(res_state statp, const char *dname, ns_class class, int opts, + char *zname, size_t zsize, char *mname, size_t msize, + rrset_ns *nsrrsp) +{ + char tname[NS_MAXDNAME]; + u_char *resp = NULL; + int n, i, ancount, nscount; + ns_sect sect; + ns_msg msg; + u_int rcode; + + /* + * Find closest enclosing SOA, even if it's for the root zone. + */ + + /* First canonicalize dname (exactly one unescaped trailing "."). */ + if (ns_makecanon(dname, tname, sizeof tname) < 0) + goto cleanup; + dname = tname; + + resp = malloc(NS_MAXMSG); + if (resp == NULL) + goto cleanup; + + /* Now grovel the subdomains, hunting for an SOA answer or auth. */ + for (;;) { + /* Leading or inter-label '.' are skipped here. */ + while (*dname == '.') + dname++; + + /* Is there an SOA? */ + n = do_query(statp, dname, class, ns_t_soa, resp, &msg); + if (n < 0) { + DPRINTF(("get_soa: do_query('%s', %s) failed (%d)", + dname, p_class(class), n)); + goto cleanup; + } + if (n > 0) { + DPRINTF(("get_soa: CNAME or DNAME found")); + sect = ns_s_max, n = 0; + } else { + rcode = ns_msg_getflag(msg, ns_f_rcode); + ancount = ns_msg_count(msg, ns_s_an); + nscount = ns_msg_count(msg, ns_s_ns); + if (ancount > 0 && rcode == ns_r_noerror) + sect = ns_s_an, n = ancount; + else if (nscount > 0) + sect = ns_s_ns, n = nscount; + else + sect = ns_s_max, n = 0; + } + for (i = 0; i < n; i++) { + const char *t; + const u_char *rdata; + ns_rr rr; + + if (ns_parserr(&msg, sect, i, &rr) < 0) { + DPRINTF(("get_soa: ns_parserr(%s, %d) failed", + p_section(sect, ns_o_query), i)); + goto cleanup; + } + if (ns_rr_type(rr) == ns_t_cname || + ns_rr_type(rr) == ns_t_dname) + break; + if (ns_rr_type(rr) != ns_t_soa || + ns_rr_class(rr) != class) + continue; + t = ns_rr_name(rr); + switch (sect) { + case ns_s_an: + if (ns_samedomain(dname, t) == 0) { + DPRINTF( + ("get_soa: ns_samedomain('%s', '%s') == 0", + dname, t) + ); + errno = EPROTOTYPE; + goto cleanup; + } + break; + case ns_s_ns: + if (ns_samename(dname, t) == 1 || + ns_samedomain(dname, t) == 0) { + DPRINTF( + ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')", + dname, t) + ); + errno = EPROTOTYPE; + goto cleanup; + } + break; + default: + abort(); + } + if (strlen(t) + 1 > zsize) { + DPRINTF(("get_soa: zname(%lu) too small (%lu)", + (unsigned long)zsize, + (unsigned long)strlen(t) + 1)); + errno = EMSGSIZE; + goto cleanup; + } + strcpy(zname, t); + rdata = ns_rr_rdata(rr); + if (ns_name_uncompress(resp, ns_msg_end(msg), rdata, + mname, msize) < 0) { + DPRINTF(("get_soa: ns_name_uncompress failed") + ); + goto cleanup; + } + if (save_ns(statp, &msg, ns_s_ns, + zname, class, opts, nsrrsp) < 0) { + DPRINTF(("get_soa: save_ns failed")); + goto cleanup; + } + free(resp); + return (0); + } + + /* If we're out of labels, then not even "." has an SOA! */ + if (*dname == '\0') + break; + + /* Find label-terminating "."; top of loop will skip it. */ + while (*dname != '.') { + if (*dname == '\\') + if (*++dname == '\0') { + errno = EMSGSIZE; + goto cleanup; + } + dname++; + } + } + DPRINTF(("get_soa: out of labels")); + errno = EDESTADDRREQ; + cleanup: + if (resp != NULL) + free(resp); + return (-1); +} + +static int +get_ns(res_state statp, const char *zname, ns_class class, int opts, + rrset_ns *nsrrsp) +{ + u_char *resp; + ns_msg msg; + int n; + + resp = malloc(NS_MAXMSG); + if (resp == NULL) + return (-1); + + /* Go and get the NS RRs for this zone. */ + n = do_query(statp, zname, class, ns_t_ns, resp, &msg); + if (n != 0) { + DPRINTF(("get_ns: do_query('%s', %s) failed (%d)", + zname, p_class(class), n)); + free(resp); + return (-1); + } + + /* Remember the NS RRs and associated A RRs that came back. */ + if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) { + DPRINTF(("get_ns save_ns('%s', %s) failed", + zname, p_class(class))); + free(resp); + return (-1); + } + + free(resp); + return (0); +} + +static int +get_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) { + rr_ns *nsrr, *nsrr_n; + u_char *resp; + + resp = malloc(NS_MAXMSG); + if (resp == NULL) + return(-1); + + /* Go and get the A RRs for each empty NS RR on our list. */ + for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) { + ns_msg msg; + int n; + + nsrr_n = NEXT(nsrr, link); + + if ((nsrr->flags & RR_NS_HAVE_V4) == 0) { + n = do_query(statp, nsrr->name, class, ns_t_a, + resp, &msg); + if (n < 0) { + DPRINTF( + ("get_glue: do_query('%s', %s') failed", + nsrr->name, p_class(class))); + goto cleanup; + } + if (n > 0) { + DPRINTF(( + "get_glue: do_query('%s', %s') CNAME or DNAME found", + nsrr->name, p_class(class))); + } + if (save_a(statp, &msg, ns_s_an, nsrr->name, class, + opts, nsrr) < 0) { + DPRINTF(("get_glue: save_r('%s', %s) failed", + nsrr->name, p_class(class))); + goto cleanup; + } + } + + if ((nsrr->flags & RR_NS_HAVE_V6) == 0) { + n = do_query(statp, nsrr->name, class, ns_t_aaaa, + resp, &msg); + if (n < 0) { + DPRINTF( + ("get_glue: do_query('%s', %s') failed", + nsrr->name, p_class(class))); + goto cleanup; + } + if (n > 0) { + DPRINTF(( + "get_glue: do_query('%s', %s') CNAME or DNAME found", + nsrr->name, p_class(class))); + } + if (save_a(statp, &msg, ns_s_an, nsrr->name, class, + opts, nsrr) < 0) { + DPRINTF(("get_glue: save_r('%s', %s) failed", + nsrr->name, p_class(class))); + goto cleanup; + } + } + + /* If it's still empty, it's just chaff. */ + if (EMPTY(nsrr->addrs)) { + DPRINTF(("get_glue: removing empty '%s' NS", + nsrr->name)); + free_nsrr(nsrrsp, nsrr); + } + } + free(resp); + return (0); + + cleanup: + free(resp); + return (-1); +} + +static int +save_ns(res_state statp, ns_msg *msg, ns_sect sect, + const char *owner, ns_class class, int opts, + rrset_ns *nsrrsp) +{ + int i; + + for (i = 0; i < ns_msg_count(*msg, sect); i++) { + char tname[MAXDNAME]; + const u_char *rdata; + rr_ns *nsrr; + ns_rr rr; + + if (ns_parserr(msg, sect, i, &rr) < 0) { + DPRINTF(("save_ns: ns_parserr(%s, %d) failed", + p_section(sect, ns_o_query), i)); + return (-1); + } + if (ns_rr_type(rr) != ns_t_ns || + ns_rr_class(rr) != class || + ns_samename(ns_rr_name(rr), owner) != 1) + continue; + nsrr = find_ns(nsrrsp, ns_rr_name(rr)); + if (nsrr == NULL) { + nsrr = malloc(sizeof *nsrr); + if (nsrr == NULL) { + DPRINTF(("save_ns: malloc failed")); + return (-1); + } + rdata = ns_rr_rdata(rr); + if (ns_name_uncompress(ns_msg_base(*msg), + ns_msg_end(*msg), rdata, + tname, sizeof tname) < 0) { + DPRINTF(("save_ns: ns_name_uncompress failed") + ); + free(nsrr); + return (-1); + } + nsrr->name = strdup(tname); + if (nsrr->name == NULL) { + DPRINTF(("save_ns: strdup failed")); + free(nsrr); + return (-1); + } + INIT_LINK(nsrr, link); + INIT_LIST(nsrr->addrs); + nsrr->flags = 0; + APPEND(*nsrrsp, nsrr, link); + } + if (save_a(statp, msg, ns_s_ar, + nsrr->name, class, opts, nsrr) < 0) { + DPRINTF(("save_ns: save_r('%s', %s) failed", + nsrr->name, p_class(class))); + return (-1); + } + } + return (0); +} + +static int +save_a(res_state statp, ns_msg *msg, ns_sect sect, + const char *owner, ns_class class, int opts, + rr_ns *nsrr) +{ + int i; + + for (i = 0; i < ns_msg_count(*msg, sect); i++) { + ns_rr rr; + rr_a *arr; + + if (ns_parserr(msg, sect, i, &rr) < 0) { + DPRINTF(("save_a: ns_parserr(%s, %d) failed", + p_section(sect, ns_o_query), i)); + return (-1); + } + if ((ns_rr_type(rr) != ns_t_a && + ns_rr_type(rr) != ns_t_aaaa) || + ns_rr_class(rr) != class || + ns_samename(ns_rr_name(rr), owner) != 1 || + ns_rr_rdlen(rr) != NS_INADDRSZ) + continue; + if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa) + continue; + if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a) + continue; + arr = malloc(sizeof *arr); + if (arr == NULL) { + DPRINTF(("save_a: malloc failed")); + return (-1); + } + INIT_LINK(arr, link); + memset(&arr->addr, 0, sizeof(arr->addr)); + switch (ns_rr_type(rr)) { + case ns_t_a: + arr->addr.sin.sin_family = AF_INET; +#ifdef HAVE_SA_LEN + arr->addr.sin.sin_len = sizeof(arr->addr.sin); +#endif + memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr), + NS_INADDRSZ); + arr->addr.sin.sin_port = htons(NAMESERVER_PORT); + nsrr->flags |= RR_NS_HAVE_V4; + break; + case ns_t_aaaa: + arr->addr.sin6.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6); +#endif + memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16); + arr->addr.sin.sin_port = htons(NAMESERVER_PORT); + nsrr->flags |= RR_NS_HAVE_V6; + break; + default: + abort(); + } + APPEND(nsrr->addrs, arr, link); + } + return (0); +} + +static void +free_nsrrset(rrset_ns *nsrrsp) { + rr_ns *nsrr; + + while ((nsrr = HEAD(*nsrrsp)) != NULL) + free_nsrr(nsrrsp, nsrr); +} + +static void +free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) { + rr_a *arr; + char *tmp; + + while ((arr = HEAD(nsrr->addrs)) != NULL) { + UNLINK(nsrr->addrs, arr, link); + free(arr); + } + DE_CONST(nsrr->name, tmp); + free(tmp); + UNLINK(*nsrrsp, nsrr, link); + free(nsrr); +} + +static rr_ns * +find_ns(rrset_ns *nsrrsp, const char *dname) { + rr_ns *nsrr; + + for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link)) + if (ns_samename(nsrr->name, dname) == 1) + return (nsrr); + return (NULL); +} + +static int +do_query(res_state statp, const char *dname, ns_class class, ns_type qtype, + u_char *resp, ns_msg *msg) +{ + u_char req[NS_PACKETSZ]; + int i, n; + + n = res_nmkquery(statp, ns_o_query, dname, class, qtype, + NULL, 0, NULL, req, NS_PACKETSZ); + if (n < 0) { + DPRINTF(("do_query: res_nmkquery failed")); + return (-1); + } + n = res_nsend(statp, req, n, resp, NS_MAXMSG); + if (n < 0) { + DPRINTF(("do_query: res_nsend failed")); + return (-1); + } + if (n == 0) { + DPRINTF(("do_query: res_nsend returned 0")); + errno = EMSGSIZE; + return (-1); + } + if (ns_initparse(resp, n, msg) < 0) { + DPRINTF(("do_query: ns_initparse failed")); + return (-1); + } + n = 0; + for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) { + ns_rr rr; + + if (ns_parserr(msg, ns_s_an, i, &rr) < 0) { + DPRINTF(("do_query: ns_parserr failed")); + return (-1); + } + n += (ns_rr_class(rr) == class && + (ns_rr_type(rr) == ns_t_cname || + ns_rr_type(rr) == ns_t_dname)); + } + return (n); +} + +static void +res_dprintf(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + fputs(";; res_findzonecut: ", stderr); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_init.c b/usr/src/lib/libresolv2_joy/common/resolv/res_init.c new file mode 100644 index 0000000000..4a7724645f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_init.c @@ -0,0 +1,953 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + */ + + +/* + * Copyright (c) 1985, 1989, 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 (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> + +#ifndef HAVE_MD5 +# include "../dst/md5.h" +#else +# ifdef SOLARIS2 +# include <sys/md5.h> +# endif +#endif +#ifndef _MD5_H_ +# define _MD5_H_ 1 /*%< make sure we do not include rsaref md5.h file */ +#endif + + +#include "port_after.h" + +/* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */ +#include <resolv_joy.h> + +/* ISC purposely put port_after.h before <resolv.h> to force in6 stuff + * (above) so we explicitly include port_resolv.h here */ +#include "port_resolv.h" + +#include "res_private.h" + +/*% Options. Should all be left alone. */ +#define RESOLVSORT +#define DEBUG + +#ifdef SUNW_INITCHKIF +#include <net/if.h> +#include <netinet/if_ether.h> +#include <sys/sockio.h> +#define MAXIFS 8192 +#endif /* SUNW_INITCHKIF */ + +#ifdef SOLARIS2 +#include <sys/systeminfo.h> +#endif + +static void res_setoptions __P((res_state, const char *, const char *)); + +#ifdef RESOLVSORT +static const char sort_mask[] = "/&"; +#define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL) +static u_int32_t net_mask __P((struct in_addr)); +#endif + +#if !defined(isascii) /*%< XXX - could be a function */ +# define isascii(c) (!(c & 0200)) +#endif + +/* + * Resolver state default settings. + */ + +/*% + * Set up default settings. If the configuration file exist, the values + * there will have precedence. Otherwise, the server address is set to + * INADDR_ANY and the default domain name comes from the gethostname(). + * + * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1 + * rather than INADDR_ANY ("0.0.0.0") as the default name server address + * since it was noted that INADDR_ANY actually meant ``the first interface + * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface, + * it had to be "up" in order for you to reach your own name server. It + * was later decided that since the recommended practice is to always + * install local static routes through 127.0.0.1 for all your network + * interfaces, that we could solve this problem without a code change. + * + * The configuration file should always be used, since it is the only way + * to specify a default domain. If you are running a server on your local + * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1" + * in the configuration file. + * + * Return 0 if completes successfully, -1 on error + */ +int +res_ninit(res_state statp) { + extern int __res_vinit(res_state, int); + return (__res_vinit(statp, 0)); +} + +/*% This function has to be reachable by res_data.c but not publically. */ +int +__res_vinit(res_state statp, int preinit) { + register FILE *fp; + register char *cp, **pp; + register int n; + char buf[BUFSIZ]; + int nserv = 0; /*%< number of nameserver records read from file */ + int haveenv = 0; + int havesearch = 0; +#ifdef RESOLVSORT + int nsort = 0; + char *net; +#endif + int dots; + union res_sockaddr_union u[2]; + int maxns = MAXNS; + + RES_SET_H_ERRNO(statp, 0); + if (statp->_u._ext.ext != NULL) + res_ndestroy(statp); + + if (!preinit) { + statp->retrans = RES_TIMEOUT; + statp->retry = RES_DFLRETRY; + statp->options = RES_DEFAULT; + res_rndinit(statp); + statp->id = res_nrandomid(statp); + } + + memset(u, 0, sizeof(u)); +#ifdef USELOOPBACK + u[nserv].sin.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1); +#else + u[nserv].sin.sin_addr.s_addr = INADDR_ANY; +#endif + u[nserv].sin.sin_family = AF_INET; + u[nserv].sin.sin_port = htons(NAMESERVER_PORT); +#ifdef HAVE_SA_LEN + u[nserv].sin.sin_len = sizeof(struct sockaddr_in); +#endif + nserv++; +#ifdef HAS_INET6_STRUCTS +#ifdef USELOOPBACK + u[nserv].sin6.sin6_addr = in6addr_loopback; +#else + u[nserv].sin6.sin6_addr = in6addr_any; +#endif + u[nserv].sin6.sin6_family = AF_INET6; + u[nserv].sin6.sin6_port = htons(NAMESERVER_PORT); +#ifdef HAVE_SA_LEN + u[nserv].sin6.sin6_len = sizeof(struct sockaddr_in6); +#endif + nserv++; +#endif + statp->nscount = 0; + statp->ndots = 1; + statp->pfcode = 0; + statp->_vcsock = -1; + statp->_flags = 0; + statp->qhook = NULL; + statp->rhook = NULL; + statp->_u._ext.nscount = 0; + statp->_u._ext.ext = malloc(sizeof(*statp->_u._ext.ext)); + if (statp->_u._ext.ext != NULL) { + memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext)); + statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr; + strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa"); + strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int"); + } else { + /* + * Historically res_init() rarely, if at all, failed. + * Examples and applications exist which do not check + * our return code. Furthermore several applications + * simply call us to get the systems domainname. So + * rather then immediately fail here we store the + * failure, which is returned later, in h_errno. And + * prevent the collection of 'nameserver' information + * by setting maxns to 0. Thus applications that fail + * to check our return code wont be able to make + * queries anyhow. + */ + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + maxns = 0; + } +#ifdef RESOLVSORT + statp->nsort = 0; +#endif + res_setservers(statp, u, nserv); + +#ifdef SUNW_INITCHKIF +/* + * Short circuit res_init() if no non-loopback interfaces are up. This is + * done to avoid boot delays if "dns" comes before "files" in nsswitch.conf. + * An additional fix has been added to this code, to count all external + * interfaces, which includes the IPv6 interfaces. If no external interfaces + * are found, an additional check is carried out to determine if any deprecated + * interfaces are up. + */ + { + int s; + struct lifnum lifn; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("res_init: socket"); + goto freedata; + } + lifn.lifn_family = AF_UNSPEC; + lifn.lifn_flags = LIFC_EXTERNAL_SOURCE; + if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) { + close(s); + goto freedata; + } + if (lifn.lifn_count == 0) { + /* + * Check if there are any deprecated interfaces up + */ + struct lifconf lifc; + uchar_t *buf; + int buflen, i, int_up = 0; + + lifn.lifn_flags = 0; + if ((ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) || + (lifn.lifn_count < 1)) { + close(s); + goto freedata; + } + + buflen = lifn.lifn_count * sizeof (struct lifreq); + buf = (uchar_t *)malloc(buflen); + if (buf == NULL) { + close(s); + goto freedata; + } + + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_flags = 0; + lifc.lifc_len = buflen; + lifc.lifc_lifcu.lifcu_buf = (caddr_t)buf; + if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { + close(s); + free(buf); + goto freedata; + } + + for (i = 0; i < lifn.lifn_count; ++i) { + struct lifreq *lreqp, lreq; + + lreqp = (struct lifreq *)&lifc.lifc_req[i]; + strlcpy(lreq.lifr_name, lreqp->lifr_name, + sizeof (lreq.lifr_name)); + if (ioctl(s, SIOCGLIFFLAGS, &lreq) < 0) { + close(s); + free(buf); + goto freedata; + } + if ((lreq.lifr_flags & IFF_UP) && + !(lreq.lifr_flags & IFF_NOLOCAL) && + !(lreq.lifr_flags & IFF_NOXMIT) && + !(lreq.lifr_flags & IFF_LOOPBACK)) { + int_up = 1; + break; + } + } + free(buf); + + if (!int_up) { + close(s); + goto freedata; + } + } + close(s); + } +#endif /* SUNW_INITCHKIF */ + +#ifdef SOLARIS2 + /* + * The old libresolv derived the defaultdomain from NIS/NIS+. + * We want to keep this behaviour + */ + { + char buf[sizeof(statp->defdname)], *cp; + int ret; + + if ((ret = sysinfo(SI_SRPC_DOMAIN, buf, sizeof(buf))) > 0 && + (unsigned int)ret <= sizeof(buf)) { + if (buf[0] == '+') + buf[0] = '.'; + cp = strchr(buf, '.'); + cp = (cp == NULL) ? buf : (cp + 1); + strncpy(statp->defdname, cp, + sizeof(statp->defdname) - 1); + statp->defdname[sizeof(statp->defdname) - 1] = '\0'; + } + } +#endif /* SOLARIS2 */ + + /* Allow user to override the local domain definition */ + if ((cp = getenv("LOCALDOMAIN")) != NULL) { + (void)strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); + statp->defdname[sizeof(statp->defdname) - 1] = '\0'; + haveenv++; + + /* + * Set search list to be blank-separated strings + * from rest of env value. Permits users of LOCALDOMAIN + * to still have a search list, and anyone to set the + * one that they want to use as an individual (even more + * important now that the rfc1535 stuff restricts searches) + */ + cp = statp->defdname; + pp = statp->dnsrch; + *pp++ = cp; + for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) { + if (*cp == '\n') /*%< silly backwards compat */ + break; + else if (*cp == ' ' || *cp == '\t') { + *cp = 0; + n = 1; + } else if (n) { + *pp++ = cp; + n = 0; + havesearch = 1; + } + } + /* null terminate last domain if there are excess */ + while (*cp != '\0' && *cp != ' ' && *cp != '\t' && *cp != '\n') + cp++; + *cp = '\0'; + *pp++ = 0; + } + +#define MATCH(line, name) \ + (!strncmp(line, name, sizeof(name) - 1) && \ + (line[sizeof(name) - 1] == ' ' || \ + line[sizeof(name) - 1] == '\t')) + + nserv = 0; + if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) { + /* read the config file */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* skip comments */ + if (*buf == ';' || *buf == '#') + continue; + /* read default domain name */ + if (MATCH(buf, "domain")) { + if (haveenv) /*%< skip if have from environ */ + continue; + cp = buf + sizeof("domain") - 1; + while (*cp == ' ' || *cp == '\t') + cp++; + if ((*cp == '\0') || (*cp == '\n')) + continue; + strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); + statp->defdname[sizeof(statp->defdname) - 1] = '\0'; + if ((cp = strpbrk(statp->defdname, " \t\n")) != NULL) + *cp = '\0'; + havesearch = 0; + continue; + } + /* set search list */ + if (MATCH(buf, "search")) { + if (haveenv) /*%< skip if have from environ */ + continue; + cp = buf + sizeof("search") - 1; + while (*cp == ' ' || *cp == '\t') + cp++; + if ((*cp == '\0') || (*cp == '\n')) + continue; + strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); + statp->defdname[sizeof(statp->defdname) - 1] = '\0'; + if ((cp = strchr(statp->defdname, '\n')) != NULL) + *cp = '\0'; + /* + * Set search list to be blank-separated strings + * on rest of line. + */ + cp = statp->defdname; + pp = statp->dnsrch; + *pp++ = cp; + for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) { + if (*cp == ' ' || *cp == '\t') { + *cp = 0; + n = 1; + } else if (n) { + *pp++ = cp; + n = 0; + } + } + /* null terminate last domain if there are excess */ + while (*cp != '\0' && *cp != ' ' && *cp != '\t') + cp++; + *cp = '\0'; + *pp++ = 0; + havesearch = 1; + continue; + } + /* read nameservers to query */ + if (MATCH(buf, "nameserver") && nserv < maxns) { + struct addrinfo hints, *ai; + char sbuf[NI_MAXSERV]; + const size_t minsiz = + sizeof(statp->_u._ext.ext->nsaddrs[0]); + + cp = buf + sizeof("nameserver") - 1; + while (*cp == ' ' || *cp == '\t') + cp++; + cp[strcspn(cp, ";# \t\n")] = '\0'; + if ((*cp != '\0') && (*cp != '\n')) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + sprintf(sbuf, "%u", NAMESERVER_PORT); + if (getaddrinfo(cp, sbuf, &hints, &ai) == 0 && + ai->ai_addrlen <= minsiz) { + if (statp->_u._ext.ext != NULL) { + memcpy(&statp->_u._ext.ext->nsaddrs[nserv], + ai->ai_addr, ai->ai_addrlen); + } + if (ai->ai_addrlen <= + sizeof(statp->nsaddr_list[nserv])) { + memcpy(&statp->nsaddr_list[nserv], + ai->ai_addr, ai->ai_addrlen); + } else + statp->nsaddr_list[nserv].sin_family = 0; + freeaddrinfo(ai); + nserv++; + } + } + continue; + } +#ifdef RESOLVSORT + if (MATCH(buf, "sortlist")) { + struct in_addr a; + + cp = buf + sizeof("sortlist") - 1; + while (nsort < MAXRESOLVSORT) { + while (*cp == ' ' || *cp == '\t') + cp++; + if (*cp == '\0' || *cp == '\n' || *cp == ';') + break; + net = cp; + while (*cp && !ISSORTMASK(*cp) && *cp != ';' && + isascii(*cp) && !isspace((unsigned char)*cp)) + cp++; + n = *cp; + *cp = 0; + if (inet_aton(net, &a)) { + statp->sort_list[nsort].addr = a; + if (ISSORTMASK(n)) { + *cp++ = n; + net = cp; + while (*cp && *cp != ';' && + isascii(*cp) && + !isspace((unsigned char)*cp)) + cp++; + n = *cp; + *cp = 0; + if (inet_aton(net, &a)) { + statp->sort_list[nsort].mask = a.s_addr; + } else { + statp->sort_list[nsort].mask = + net_mask(statp->sort_list[nsort].addr); + } + } else { + statp->sort_list[nsort].mask = + net_mask(statp->sort_list[nsort].addr); + } + nsort++; + } + *cp = n; + } + continue; + } +#endif + if (MATCH(buf, "options")) { + res_setoptions(statp, buf + sizeof("options") - 1, "conf"); + continue; + } + } + if (nserv > 0) + statp->nscount = nserv; +#ifdef RESOLVSORT + statp->nsort = nsort; +#endif + (void) fclose(fp); + } +/* + * Last chance to get a nameserver. This should not normally + * be necessary + */ +#ifdef NO_RESOLV_CONF + if(nserv == 0) + nserv = get_nameservers(statp); +#endif + + if (statp->defdname[0] == 0 && + gethostname(buf, sizeof(statp->defdname) - 1) == 0 && + (cp = strchr(buf, '.')) != NULL) + strcpy(statp->defdname, cp + 1); + + /* find components of local domain that might be searched */ + if (havesearch == 0) { + pp = statp->dnsrch; + *pp++ = statp->defdname; + *pp = NULL; + + dots = 0; + for (cp = statp->defdname; *cp; cp++) + dots += (*cp == '.'); + + cp = statp->defdname; + while (pp < statp->dnsrch + MAXDFLSRCH) { + if (dots < LOCALDOMAINPARTS) + break; + cp = strchr(cp, '.') + 1; /*%< we know there is one */ + *pp++ = cp; + dots--; + } + *pp = NULL; +#ifdef DEBUG + if (statp->options & RES_DEBUG) { + printf(";; res_init()... default dnsrch list:\n"); + for (pp = statp->dnsrch; *pp; pp++) + printf(";;\t%s\n", *pp); + printf(";;\t..END..\n"); + } +#endif + } + + if ((cp = getenv("RES_OPTIONS")) != NULL) + res_setoptions(statp, cp, "env"); + statp->options |= RES_INIT; + return (statp->res_h_errno); +#ifdef SUNW_INITCHKIF +freedata: + RES_SET_H_ERRNO(statp, NETDB_INTERNAL); + if (statp->_u._ext.ext != NULL) { + free(statp->_u._ext.ext); + statp->_u._ext.ext = NULL; + } + return (-1); +#endif /* SUNW_INITCHKIF */ + +} + +static void +res_setoptions(res_state statp, const char *options, const char *source) +{ + const char *cp = options; + int i; + struct __res_state_ext *ext = statp->_u._ext.ext; + +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_setoptions(\"%s\", \"%s\")...\n", + options, source); +#endif + while (*cp) { + /* skip leading and inner runs of spaces */ + while (*cp == ' ' || *cp == '\t') + cp++; + /* search for and process individual options */ + if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) { + i = atoi(cp + sizeof("ndots:") - 1); + if (i <= RES_MAXNDOTS) + statp->ndots = i; + else + statp->ndots = RES_MAXNDOTS; +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";;\tndots=%d\n", statp->ndots); +#endif + } else if (!strncmp(cp, "timeout:", sizeof("timeout:") - 1)) { + i = atoi(cp + sizeof("timeout:") - 1); + if (i <= RES_MAXRETRANS) + statp->retrans = i; + else + statp->retrans = RES_MAXRETRANS; +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";;\ttimeout=%d\n", statp->retrans); +#endif +#ifdef SOLARIS2 + } else if (!strncmp(cp, "retrans:", sizeof("retrans:") - 1)) { + /* + * For backward compatibility, 'retrans' is + * supported as an alias for 'timeout', though + * without an imposed maximum. + */ + statp->retrans = atoi(cp + sizeof("retrans:") - 1); + } else if (!strncmp(cp, "retry:", sizeof("retry:") - 1)){ + /* + * For backward compatibility, 'retry' is + * supported as an alias for 'attempts', though + * without an imposed maximum. + */ + statp->retry = atoi(cp + sizeof("retry:") - 1); +#endif /* SOLARIS2 */ + } else if (!strncmp(cp, "attempts:", sizeof("attempts:") - 1)){ + i = atoi(cp + sizeof("attempts:") - 1); + if (i <= RES_MAXRETRY) + statp->retry = i; + else + statp->retry = RES_MAXRETRY; +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";;\tattempts=%d\n", statp->retry); +#endif + } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) { +#ifdef DEBUG + if (!(statp->options & RES_DEBUG)) { + printf(";; res_setoptions(\"%s\", \"%s\")..\n", + options, source); + statp->options |= RES_DEBUG; + } + printf(";;\tdebug\n"); +#endif + } else if (!strncmp(cp, "no_tld_query", + sizeof("no_tld_query") - 1) || + !strncmp(cp, "no-tld-query", + sizeof("no-tld-query") - 1)) { + statp->options |= RES_NOTLDQUERY; + } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) { + statp->options |= RES_USE_INET6; + } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) { + statp->options |= RES_ROTATE; + } else if (!strncmp(cp, "no-check-names", + sizeof("no-check-names") - 1)) { + statp->options |= RES_NOCHECKNAME; + } +#ifdef RES_USE_EDNS0 + else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) { + statp->options |= RES_USE_EDNS0; + } +#endif + else if (!strncmp(cp, "dname", sizeof("dname") - 1)) { + statp->options |= RES_USE_DNAME; + } + else if (!strncmp(cp, "nibble:", sizeof("nibble:") - 1)) { + if (ext == NULL) + goto skip; + cp += sizeof("nibble:") - 1; + i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix) - 1); + strncpy(ext->nsuffix, cp, i); + ext->nsuffix[i] = '\0'; + } + else if (!strncmp(cp, "nibble2:", sizeof("nibble2:") - 1)) { + if (ext == NULL) + goto skip; + cp += sizeof("nibble2:") - 1; + i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix2) - 1); + strncpy(ext->nsuffix2, cp, i); + ext->nsuffix2[i] = '\0'; + } + else if (!strncmp(cp, "v6revmode:", sizeof("v6revmode:") - 1)) { + cp += sizeof("v6revmode:") - 1; + /* "nibble" and "bitstring" used to be valid */ + if (!strncmp(cp, "single", sizeof("single") - 1)) { + statp->options |= RES_NO_NIBBLE2; + } else if (!strncmp(cp, "both", sizeof("both") - 1)) { + statp->options &= + ~RES_NO_NIBBLE2; + } + } + else { + /* XXX - print a warning here? */ + } + skip: + /* skip to next run of spaces */ + while (*cp && *cp != ' ' && *cp != '\t') + cp++; + } +} + +#ifdef RESOLVSORT +/* XXX - should really support CIDR which means explicit masks always. */ +static u_int32_t +net_mask(in) /*!< XXX - should really use system's version of this */ + struct in_addr in; +{ + register u_int32_t i = ntohl(in.s_addr); + + if (IN_CLASSA(i)) + return (htonl(IN_CLASSA_NET)); + else if (IN_CLASSB(i)) + return (htonl(IN_CLASSB_NET)); + return (htonl(IN_CLASSC_NET)); +} +#endif + +void +res_rndinit(res_state statp) +{ + struct timeval now; + u_int32_t u32; + u_int16_t u16; + + gettimeofday(&now, NULL); + u32 = now.tv_sec; + memcpy(statp->_u._ext._rnd, &u32, 4); + u32 = now.tv_usec; + memcpy(statp->_u._ext._rnd + 4, &u32, 4); + u32 += now.tv_sec; + memcpy(statp->_u._ext._rnd + 8, &u32, 4); + u16 = getpid(); + memcpy(statp->_u._ext._rnd + 12, &u16, 2); + +} + +u_int +res_nrandomid(res_state statp) { + struct timeval now; + u_int16_t u16; + MD5_CTX ctx; + + gettimeofday(&now, NULL); + u16 = (u_int16_t) (now.tv_sec ^ now.tv_usec); + + memcpy(statp->_u._ext._rnd + 14, &u16, 2); +#ifndef HAVE_MD5 + MD5_Init(&ctx); + MD5_Update(&ctx, statp->_u._ext._rnd, 16); + MD5_Final(statp->_u._ext._rnd, &ctx); +#else + MD5Init(&ctx); + MD5Update(&ctx, statp->_u._ext._rnd, 16); + MD5Final(statp->_u._ext._rnd, &ctx); +#endif + memcpy(&u16, statp->_u._ext._rnd + 14, 2); + return ((u_int) u16); +} + +/*% + * This routine is for closing the socket if a virtual circuit is used and + * the program wants to close it. This provides support for endhostent() + * which expects to close the socket. + * + * This routine is not expected to be user visible. + */ +void +res_nclose(res_state statp) { + int ns; + + if (statp->_vcsock >= 0) { + (void) close(statp->_vcsock); + statp->_vcsock = -1; + statp->_flags &= ~(RES_F_VC | RES_F_CONN); + } + for (ns = 0; ns < statp->_u._ext.nscount; ns++) { + if (statp->_u._ext.nssocks[ns] != -1) { + (void) close(statp->_u._ext.nssocks[ns]); + statp->_u._ext.nssocks[ns] = -1; + } + } +} + +void +res_ndestroy(res_state statp) { + res_nclose(statp); + if (statp->_u._ext.ext != NULL) + free(statp->_u._ext.ext); + statp->options &= ~RES_INIT; + statp->_u._ext.ext = NULL; +} + +const char * +res_get_nibblesuffix(res_state statp) { + if (statp->_u._ext.ext) + return (statp->_u._ext.ext->nsuffix); + return ("ip6.arpa"); +} + +const char * +res_get_nibblesuffix2(res_state statp) { + if (statp->_u._ext.ext) + return (statp->_u._ext.ext->nsuffix2); + return ("ip6.int"); +} + +void +res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt) { + int i, nserv; + size_t size; + + /* close open servers */ + res_nclose(statp); + + /* cause rtt times to be forgotten */ + statp->_u._ext.nscount = 0; + + nserv = 0; + for (i = 0; i < cnt && nserv < MAXNS; i++) { + switch (set->sin.sin_family) { + case AF_INET: + size = sizeof(set->sin); + if (statp->_u._ext.ext) + memcpy(&statp->_u._ext.ext->nsaddrs[nserv], + &set->sin, size); + if (size <= sizeof(statp->nsaddr_list[nserv])) + memcpy(&statp->nsaddr_list[nserv], + &set->sin, size); + else + statp->nsaddr_list[nserv].sin_family = 0; + nserv++; + break; + +#ifdef HAS_INET6_STRUCTS + case AF_INET6: + size = sizeof(set->sin6); + if (statp->_u._ext.ext) + memcpy(&statp->_u._ext.ext->nsaddrs[nserv], + &set->sin6, size); + if (size <= sizeof(statp->nsaddr_list[nserv])) + memcpy(&statp->nsaddr_list[nserv], + &set->sin6, size); + else + statp->nsaddr_list[nserv].sin_family = 0; + nserv++; + break; +#endif + + default: + break; + } + set++; + } + statp->nscount = nserv; + +} + +int +res_getservers(res_state statp, union res_sockaddr_union *set, int cnt) { + int i; + size_t size; + u_int16_t family; + + for (i = 0; i < statp->nscount && i < cnt; i++) { + if (statp->_u._ext.ext) + family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family; + else + family = statp->nsaddr_list[i].sin_family; + + switch (family) { + case AF_INET: + size = sizeof(set->sin); + if (statp->_u._ext.ext) + memcpy(&set->sin, + &statp->_u._ext.ext->nsaddrs[i], + size); + else + memcpy(&set->sin, &statp->nsaddr_list[i], + size); + break; + +#ifdef HAS_INET6_STRUCTS + case AF_INET6: + size = sizeof(set->sin6); + if (statp->_u._ext.ext) + memcpy(&set->sin6, + &statp->_u._ext.ext->nsaddrs[i], + size); + else + memcpy(&set->sin6, &statp->nsaddr_list[i], + size); + break; +#endif + + default: + set->sin.sin_family = 0; + break; + } + set++; + } + return (statp->nscount); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c b/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c new file mode 100644 index 0000000000..29cfea2786 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c @@ -0,0 +1,381 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Portions Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1996, 1997, 1988, 1999, 2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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 (c) 1985, 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. + */ + +#include "port_before.h" +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/nameser.h> + +#ifdef SUNW_CONFCHECK +#include <sys/socket.h> +#include <errno.h> +#include <sys/stat.h> +#endif /* SUNW_CONFCHECK */ + + +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <string.h> +#include "port_after.h" + +/* Options. Leave them on. */ +#define DEBUG + +extern const char *_res_opcodes[]; + +#ifdef SUNW_CONFCHECK +static int _confcheck(res_state statp); +#endif /* SUNW_CONFCHECK */ + + +/*% + * Form all types of queries. + * Returns the size of the result or -1. + */ +int +res_nmkquery(res_state statp, + int op, /*!< opcode of query */ + const char *dname, /*!< domain name */ + int class, int type, /*!< class and type of query */ + const u_char *data, /*!< resource record data */ + int datalen, /*!< length of data */ + const u_char *newrr_in, /*!< new rr for modify or append */ + u_char *buf, /*!< buffer to put query */ + int buflen) /*!< size of buffer */ +{ + register HEADER *hp; + register u_char *cp, *ep; + register int n; + u_char *dnptrs[20], **dpp, **lastdnptr; + + UNUSED(newrr_in); + +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_nmkquery(%s, %s, %s, %s)\n", + _res_opcodes[op], dname, p_class(class), p_type(type)); +#endif + +#ifdef SUNW_CONFCHECK + /* + * 1247019, 1265838, and 4034368: Check to see if we can + * bailout quickly. + */ + if (_confcheck(statp) == -1) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return(-1); + } +#endif /* SUNW_CONFCHECK */ + + /* + * Initialize header fields. + */ + if ((buf == NULL) || (buflen < HFIXEDSZ)) + return (-1); + memset(buf, 0, HFIXEDSZ); + hp = (HEADER *) buf; + statp->id = res_nrandomid(statp); + hp->id = htons(statp->id); + hp->opcode = op; + hp->rd = (statp->options & RES_RECURSE) != 0U; + hp->rcode = NOERROR; + cp = buf + HFIXEDSZ; + ep = buf + buflen; + dpp = dnptrs; + *dpp++ = buf; + *dpp++ = NULL; + lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; + /* + * perform opcode specific processing + */ + switch (op) { + case QUERY: /*FALLTHROUGH*/ + case NS_NOTIFY_OP: + if (ep - cp < QFIXEDSZ) + return (-1); + if ((n = dn_comp(dname, cp, ep - cp - QFIXEDSZ, dnptrs, + lastdnptr)) < 0) + return (-1); + cp += n; + ns_put16(type, cp); + cp += INT16SZ; + ns_put16(class, cp); + cp += INT16SZ; + hp->qdcount = htons(1); + if (op == QUERY || data == NULL) + break; + /* + * Make an additional record for completion domain. + */ + if ((ep - cp) < RRFIXEDSZ) + return (-1); + n = dn_comp((const char *)data, cp, ep - cp - RRFIXEDSZ, + dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ns_put16(T_NULL, cp); + cp += INT16SZ; + ns_put16(class, cp); + cp += INT16SZ; + ns_put32(0, cp); + cp += INT32SZ; + ns_put16(0, cp); + cp += INT16SZ; + hp->arcount = htons(1); + break; + + case IQUERY: + /* + * Initialize answer section + */ + if (ep - cp < 1 + RRFIXEDSZ + datalen) + return (-1); + *cp++ = '\0'; /*%< no domain name */ + ns_put16(type, cp); + cp += INT16SZ; + ns_put16(class, cp); + cp += INT16SZ; + ns_put32(0, cp); + cp += INT32SZ; + ns_put16(datalen, cp); + cp += INT16SZ; + if (datalen) { + memcpy(cp, data, datalen); + cp += datalen; + } + hp->ancount = htons(1); + break; + + default: + return (-1); + } + return (cp - buf); +} + +#ifdef RES_USE_EDNS0 +/* attach OPT pseudo-RR, as documented in RFC2671 (EDNS0). */ + +int +res_nopt(res_state statp, + int n0, /*%< current offset in buffer */ + u_char *buf, /*%< buffer to put query */ + int buflen, /*%< size of buffer */ + int anslen) /*%< UDP answer buffer size */ +{ + register HEADER *hp; + register u_char *cp, *ep; + u_int16_t flags = 0; + +#ifdef DEBUG + if ((statp->options & RES_DEBUG) != 0U) + printf(";; res_nopt()\n"); +#endif + + hp = (HEADER *) buf; + cp = buf + n0; + ep = buf + buflen; + + if ((ep - cp) < 1 + RRFIXEDSZ) + return (-1); + + *cp++ = 0; /*%< "." */ + ns_put16(ns_t_opt, cp); /*%< TYPE */ + cp += INT16SZ; + ns_put16(anslen & 0xffff, cp); /*%< CLASS = UDP payload size */ + cp += INT16SZ; + *cp++ = NOERROR; /*%< extended RCODE */ + *cp++ = 0; /*%< EDNS version */ + + if (statp->options & RES_USE_DNSSEC) { +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_opt()... ENDS0 DNSSEC\n"); +#endif + flags |= NS_OPT_DNSSEC_OK; + } + ns_put16(flags, cp); + cp += INT16SZ; + + ns_put16(0U, cp); /*%< RDLEN */ + cp += INT16SZ; + + hp->arcount = htons(ntohs(hp->arcount) + 1); + + return (cp - buf); +} + +/* + * Construct variable data (RDATA) block for OPT psuedo-RR, append it + * to the buffer, then update the RDLEN field (previously set to zero by + * res_nopt()) with the new RDATA length. + */ +int +res_nopt_rdata(res_state statp, + int n0, /*%< current offset in buffer */ + u_char *buf, /*%< buffer to put query */ + int buflen, /*%< size of buffer */ + u_char *rdata, /*%< ptr to start of opt rdata */ + u_short code, /*%< OPTION-CODE */ + u_short len, /*%< OPTION-LENGTH */ + u_char *data) /*%< OPTION_DATA */ +{ + register u_char *cp, *ep; + +#ifdef DEBUG + if ((statp->options & RES_DEBUG) != 0U) + printf(";; res_nopt_rdata()\n"); +#endif + + cp = buf + n0; + ep = buf + buflen; + + if ((ep - cp) < (4 + len)) + return (-1); + + if (rdata < (buf + 2) || rdata >= ep) + return (-1); + + ns_put16(code, cp); + cp += INT16SZ; + + ns_put16(len, cp); + cp += INT16SZ; + + memcpy(cp, data, len); + cp += len; + + len = cp - rdata; + ns_put16(len, rdata - 2); /* Update RDLEN field */ + + return (cp - buf); +} +#endif + +#ifdef SUNW_CONFCHECK + +/* + * Time out quickly if there is no /etc/resolv.conf and a TCP connection + * to the local DNS server fails. + */ +static int _confcheck(res_state statp) +{ + int ns; + struct stat rc_stat; + struct sockaddr_in ns_sin; + + /* First, we check to see if /etc/resolv.conf exists. + * If it doesn't, then it is likely that the localhost is + * the nameserver. + */ + if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) { + + /* Next, we check to see if _res.nsaddr is set to loopback. + * If it isn't, it has been altered by the application + * explicitly and we then want to bail with success. + */ + if (statp->nsaddr.sin_addr.S_un.S_addr == + htonl(INADDR_LOOPBACK)) { + + /* Lastly, we try to connect to the TCP port of the + * nameserver. If this fails, then we know that + * DNS is misconfigured and we can quickly exit. + */ + ns = socket(AF_INET, SOCK_STREAM, 0); + IN_SET_LOOPBACK_ADDR(&ns_sin); + ns_sin.sin_port = htons(NAMESERVER_PORT); + if (connect(ns, (struct sockaddr *) &ns_sin, + sizeof ns_sin) == -1) { + close(ns); + return(-1); + } + else { + close(ns); + + return(0); + } + } + + return(0); + } + + return (0); +} +#endif /* SUNW_CONFCHECK */ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c new file mode 100644 index 0000000000..686718b42f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c @@ -0,0 +1,1159 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief + * Based on the Dynamic DNS reference implementation by Viraj Bais + * <viraj_bais@ccm.fm.intel.com> + */ + +#include "port_before.h" + +#include <sys/types.h> +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <res_update.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "port_after.h" + +/* Options. Leave them on. */ +#define DEBUG +#define MAXPORT 1024 + +static int getnum_str(u_char **, u_char *); +static int gethexnum_str(u_char **, u_char *); +static int getword_str(char *, int, u_char **, u_char *); +static int getstr_str(char *, int, u_char **, u_char *); + +#define ShrinkBuffer(x) if ((buflen -= x) < 0) return (-2); + +/* Forward. */ + +int res_protocolnumber(const char *); +int res_servicenumber(const char *); + +/*% + * Form update packets. + * Returns the size of the resulting packet if no error + * + * On error, + * returns + *\li -1 if error in reading a word/number in rdata + * portion for update packets + *\li -2 if length of buffer passed is insufficient + *\li -3 if zone section is not the first section in + * the linked list, or section order has a problem + *\li -4 on a number overflow + *\li -5 unknown operation or no records + */ +int +res_nmkupdate(res_state statp, ns_updrec *rrecp_in, u_char *buf, int buflen) { + ns_updrec *rrecp_start = rrecp_in; + HEADER *hp; + u_char *cp, *sp2, *startp, *endp; + int n, i, soanum, multiline; + ns_updrec *rrecp; + struct in_addr ina; + struct in6_addr in6a; + char buf2[MAXDNAME]; + u_char buf3[MAXDNAME]; + int section, numrrs = 0, counts[ns_s_max]; + u_int16_t rtype, rclass; + u_int32_t n1, rttl; + u_char *dnptrs[20], **dpp, **lastdnptr; + int siglen, keylen, certlen; + + /* + * Initialize header fields. + */ + if ((buf == NULL) || (buflen < HFIXEDSZ)) + return (-1); + memset(buf, 0, HFIXEDSZ); + hp = (HEADER *) buf; + statp->id = res_nrandomid(statp); + hp->id = htons(statp->id); + hp->opcode = ns_o_update; + hp->rcode = NOERROR; + cp = buf + HFIXEDSZ; + buflen -= HFIXEDSZ; + dpp = dnptrs; + *dpp++ = buf; + *dpp++ = NULL; + lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; + + if (rrecp_start == NULL) + return (-5); + else if (rrecp_start->r_section != S_ZONE) + return (-3); + + memset(counts, 0, sizeof counts); + for (rrecp = rrecp_start; rrecp; rrecp = NEXT(rrecp, r_glink)) { + numrrs++; + section = rrecp->r_section; + if (section < 0 || section >= ns_s_max) + return (-1); + counts[section]++; + for (i = section + 1; i < ns_s_max; i++) + if (counts[i]) + return (-3); + rtype = rrecp->r_type; + rclass = rrecp->r_class; + rttl = rrecp->r_ttl; + /* overload class and type */ + if (section == S_PREREQ) { + rttl = 0; + switch (rrecp->r_opcode) { + case YXDOMAIN: + rclass = C_ANY; + rtype = T_ANY; + rrecp->r_size = 0; + break; + case NXDOMAIN: + rclass = C_NONE; + rtype = T_ANY; + rrecp->r_size = 0; + break; + case NXRRSET: + rclass = C_NONE; + rrecp->r_size = 0; + break; + case YXRRSET: + if (rrecp->r_size == 0) + rclass = C_ANY; + break; + default: + fprintf(stderr, + "res_mkupdate: incorrect opcode: %d\n", + rrecp->r_opcode); + fflush(stderr); + return (-1); + } + } else if (section == S_UPDATE) { + switch (rrecp->r_opcode) { + case DELETE: + rclass = rrecp->r_size == 0 ? C_ANY : C_NONE; + break; + case ADD: + break; + default: + fprintf(stderr, + "res_mkupdate: incorrect opcode: %d\n", + rrecp->r_opcode); + fflush(stderr); + return (-1); + } + } + + /* + * XXX appending default domain to owner name is omitted, + * fqdn must be provided + */ + if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs, + lastdnptr)) < 0) + return (-1); + cp += n; + ShrinkBuffer(n + 2*INT16SZ); + PUTSHORT(rtype, cp); + PUTSHORT(rclass, cp); + if (section == S_ZONE) { + if (numrrs != 1 || rrecp->r_type != T_SOA) + return (-3); + continue; + } + ShrinkBuffer(INT32SZ + INT16SZ); + PUTLONG(rttl, cp); + sp2 = cp; /*%< save pointer to length byte */ + cp += INT16SZ; + if (rrecp->r_size == 0) { + if (section == S_UPDATE && rclass != C_ANY) + return (-1); + else { + PUTSHORT(0, sp2); + continue; + } + } + startp = rrecp->r_data; + endp = startp + rrecp->r_size - 1; + /* XXX this should be done centrally. */ + switch (rrecp->r_type) { + case T_A: + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + if (!inet_aton(buf2, &ina)) + return (-1); + n1 = ntohl(ina.s_addr); + ShrinkBuffer(INT32SZ); + PUTLONG(n1, cp); + break; + case T_CNAME: + case T_MB: + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + case ns_t_dname: + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + break; + case T_MINFO: + case T_SOA: + case T_RP: + for (i = 0; i < 2; i++) { + if (!getword_str(buf2, sizeof buf2, &startp, + endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, + dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + } + if (rrecp->r_type == T_SOA) { + ShrinkBuffer(5 * INT32SZ); + while (isspace(*startp) || !*startp) + startp++; + if (*startp == '(') { + multiline = 1; + startp++; + } else + multiline = 0; + /* serial, refresh, retry, expire, minimum */ + for (i = 0; i < 5; i++) { + soanum = getnum_str(&startp, endp); + if (soanum < 0) + return (-1); + PUTLONG(soanum, cp); + } + if (multiline) { + while (isspace(*startp) || !*startp) + startp++; + if (*startp != ')') + return (-1); + } + } + break; + case T_MX: + case T_AFSDB: + case T_RT: + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + break; + case T_SRV: + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, NULL, NULL); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + break; + case T_PX: + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + PUTSHORT(n, cp); + ShrinkBuffer(INT16SZ); + for (i = 0; i < 2; i++) { + if (!getword_str(buf2, sizeof buf2, &startp, + endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, + lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + } + break; + case T_WKS: { + char bm[MAXPORT/8]; + unsigned int maxbm = 0; + + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + if (!inet_aton(buf2, &ina)) + return (-1); + n1 = ntohl(ina.s_addr); + ShrinkBuffer(INT32SZ); + PUTLONG(n1, cp); + + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + if ((i = res_protocolnumber(buf2)) < 0) + return (-1); + ShrinkBuffer(1); + *cp++ = i & 0xff; + + for (i = 0; i < MAXPORT/8 ; i++) + bm[i] = 0; + + while (getword_str(buf2, sizeof buf2, &startp, endp)) { + if ((n = res_servicenumber(buf2)) <= 0) + return (-1); + + if (n < MAXPORT) { + bm[n/8] |= (0x80>>(n%8)); + if ((unsigned)n > maxbm) + maxbm = n; + } else + return (-1); + } + maxbm = maxbm/8 + 1; + ShrinkBuffer(maxbm); + memcpy(cp, bm, maxbm); + cp += maxbm; + break; + } + case T_HINFO: + for (i = 0; i < 2; i++) { + if ((n = getstr_str(buf2, sizeof buf2, + &startp, endp)) < 0) + return (-1); + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + } + break; + case T_TXT: + for (;;) { + if ((n = getstr_str(buf2, sizeof buf2, + &startp, endp)) < 0) { + if (cp != (sp2 + INT16SZ)) + break; + return (-1); + } + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + } + break; + case T_X25: + /* RFC1183 */ + if ((n = getstr_str(buf2, sizeof buf2, &startp, + endp)) < 0) + return (-1); + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + break; + case T_ISDN: + /* RFC1183 */ + if ((n = getstr_str(buf2, sizeof buf2, &startp, + endp)) < 0) + return (-1); + if ((n > 255) || (n == 0)) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + if ((n = getstr_str(buf2, sizeof buf2, &startp, + endp)) < 0) + n = 0; + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + break; + case T_NSAP: + if ((n = inet_nsap_addr((char *)startp, (u_char *)buf2, sizeof(buf2))) != 0) { + ShrinkBuffer(n); + memcpy(cp, buf2, n); + cp += n; + } else { + return (-1); + } + break; + case T_LOC: + if ((n = loc_aton((char *)startp, (u_char *)buf2)) != 0) { + ShrinkBuffer(n); + memcpy(cp, buf2, n); + cp += n; + } else + return (-1); + break; + case ns_t_sig: + { + int sig_type, success, dateerror; + u_int32_t exptime, timesigned; + + /* type */ + if ((n = getword_str(buf2, sizeof buf2, + &startp, endp)) < 0) + return (-1); + sig_type = sym_ston(__p_type_syms, buf2, &success); + if (!success || sig_type == ns_t_any) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(sig_type, cp); + /* alg */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(1); + *cp++ = n; + /* labels */ + n = getnum_str(&startp, endp); + if (n <= 0 || n > 255) + return (-1); + ShrinkBuffer(1); + *cp++ = n; + /* ottl & expire */ + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + exptime = ns_datetosecs(buf2, &dateerror); + if (!dateerror) { + ShrinkBuffer(INT32SZ); + PUTLONG(rttl, cp); + } + else { + char *ulendp; + u_int32_t ottl; + + errno = 0; + ottl = strtoul(buf2, &ulendp, 10); + if (errno != 0 || + (ulendp != NULL && *ulendp != '\0')) + return (-1); + ShrinkBuffer(INT32SZ); + PUTLONG(ottl, cp); + if (!getword_str(buf2, sizeof buf2, &startp, + endp)) + return (-1); + exptime = ns_datetosecs(buf2, &dateerror); + if (dateerror) + return (-1); + } + /* expire */ + ShrinkBuffer(INT32SZ); + PUTLONG(exptime, cp); + /* timesigned */ + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + timesigned = ns_datetosecs(buf2, &dateerror); + if (!dateerror) { + ShrinkBuffer(INT32SZ); + PUTLONG(timesigned, cp); + } + else + return (-1); + /* footprint */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + /* signer name */ + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + /* sig */ + if ((n = getword_str(buf2, sizeof buf2, + &startp, endp)) < 0) + return (-1); + siglen = b64_pton(buf2, buf3, sizeof(buf3)); + if (siglen < 0) + return (-1); + ShrinkBuffer(siglen); + memcpy(cp, buf3, siglen); + cp += siglen; + break; + } + case ns_t_key: + /* flags */ + n = gethexnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + /* proto */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(1); + *cp++ = n; + /* alg */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(1); + *cp++ = n; + /* key */ + if ((n = getword_str(buf2, sizeof buf2, + &startp, endp)) < 0) + return (-1); + keylen = b64_pton(buf2, buf3, sizeof(buf3)); + if (keylen < 0) + return (-1); + ShrinkBuffer(keylen); + memcpy(cp, buf3, keylen); + cp += keylen; + break; + case ns_t_nxt: + { + int success, nxt_type; + u_char data[32]; + int maxtype; + + /* next name */ + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, NULL, NULL); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + maxtype = 0; + memset(data, 0, sizeof data); + for (;;) { + if (!getword_str(buf2, sizeof buf2, &startp, + endp)) + break; + nxt_type = sym_ston(__p_type_syms, buf2, + &success); + if (!success || !ns_t_rr_p(nxt_type)) + return (-1); + NS_NXT_BIT_SET(nxt_type, data); + if (nxt_type > maxtype) + maxtype = nxt_type; + } + n = maxtype/NS_NXT_BITS+1; + ShrinkBuffer(n); + memcpy(cp, data, n); + cp += n; + break; + } + case ns_t_cert: + /* type */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + /* key tag */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + /* alg */ + n = getnum_str(&startp, endp); + if (n < 0) + return (-1); + ShrinkBuffer(1); + *cp++ = n; + /* cert */ + if ((n = getword_str(buf2, sizeof buf2, + &startp, endp)) < 0) + return (-1); + certlen = b64_pton(buf2, buf3, sizeof(buf3)); + if (certlen < 0) + return (-1); + ShrinkBuffer(certlen); + memcpy(cp, buf3, certlen); + cp += certlen; + break; + case ns_t_aaaa: + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + if (inet_pton(AF_INET6, buf2, &in6a) <= 0) + return (-1); + ShrinkBuffer(NS_IN6ADDRSZ); + memcpy(cp, &in6a, NS_IN6ADDRSZ); + cp += NS_IN6ADDRSZ; + break; + case ns_t_naptr: + /* Order Preference Flags Service Replacement Regexp */ + /* Order */ + n = getnum_str(&startp, endp); + if (n < 0 || n > 65535) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + /* Preference */ + n = getnum_str(&startp, endp); + if (n < 0 || n > 65535) + return (-1); + ShrinkBuffer(INT16SZ); + PUTSHORT(n, cp); + /* Flags */ + if ((n = getstr_str(buf2, sizeof buf2, + &startp, endp)) < 0) { + return (-1); + } + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + /* Service Classes */ + if ((n = getstr_str(buf2, sizeof buf2, + &startp, endp)) < 0) { + return (-1); + } + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + /* Pattern */ + if ((n = getstr_str(buf2, sizeof buf2, + &startp, endp)) < 0) { + return (-1); + } + if (n > 255) + return (-1); + ShrinkBuffer(n+1); + *cp++ = n; + memcpy(cp, buf2, n); + cp += n; + /* Replacement */ + if (!getword_str(buf2, sizeof buf2, &startp, endp)) + return (-1); + n = dn_comp(buf2, cp, buflen, NULL, NULL); + if (n < 0) + return (-1); + cp += n; + ShrinkBuffer(n); + break; + default: + return (-1); + } /*switch*/ + n = (u_int16_t)((cp - sp2) - INT16SZ); + PUTSHORT(n, sp2); + } /*for*/ + + hp->qdcount = htons(counts[0]); + hp->ancount = htons(counts[1]); + hp->nscount = htons(counts[2]); + hp->arcount = htons(counts[3]); + return (cp - buf); +} + +/*% + * Get a whitespace delimited word from a string (not file) + * into buf. modify the start pointer to point after the + * word in the string. + */ +static int +getword_str(char *buf, int size, u_char **startpp, u_char *endp) { + char *cp; + int c; + + for (cp = buf; *startpp <= endp; ) { + c = **startpp; + if (isspace(c) || c == '\0') { + if (cp != buf) /*%< trailing whitespace */ + break; + else { /*%< leading whitespace */ + (*startpp)++; + continue; + } + } + (*startpp)++; + if (cp >= buf+size-1) + break; + *cp++ = (u_char)c; + } + *cp = '\0'; + return (cp != buf); +} + +/*% + * get a white spae delimited string from memory. Process quoted strings + * and \\DDD escapes. Return length or -1 on error. Returned string may + * contain nulls. + */ +static char digits[] = "0123456789"; +static int +getstr_str(char *buf, int size, u_char **startpp, u_char *endp) { + char *cp; + int c, c1 = 0; + int inquote = 0; + int seen_quote = 0; + int escape = 0; + int dig = 0; + + for (cp = buf; *startpp <= endp; ) { + if ((c = **startpp) == '\0') + break; + /* leading white space */ + if ((cp == buf) && !seen_quote && isspace(c)) { + (*startpp)++; + continue; + } + + switch (c) { + case '\\': + if (!escape) { + escape = 1; + dig = 0; + c1 = 0; + (*startpp)++; + continue; + } + goto do_escape; + case '"': + if (!escape) { + inquote = !inquote; + seen_quote = 1; + (*startpp)++; + continue; + } + /* fall through */ + default: + do_escape: + if (escape) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + c1 = c1 * 10 + + (strchr(digits, c) - digits); + + if (++dig == 3) { + c = c1 &0xff; + break; + } + (*startpp)++; + continue; + } + escape = 0; + } else if (!inquote && isspace(c)) + goto done; + if (cp >= buf+size-1) + goto done; + *cp++ = (u_char)c; + (*startpp)++; + } + } + done: + *cp = '\0'; + return ((cp == buf)? (seen_quote? 0: -1): (cp - buf)); +} + +/*% + * Get a whitespace delimited base 16 number from a string (not file) into buf + * update the start pointer to point after the number in the string. + */ +static int +gethexnum_str(u_char **startpp, u_char *endp) { + int c, n; + int seendigit = 0; + int m = 0; + + if (*startpp + 2 >= endp || strncasecmp((char *)*startpp, "0x", 2) != 0) + return getnum_str(startpp, endp); + (*startpp)+=2; + for (n = 0; *startpp <= endp; ) { + c = **startpp; + if (isspace(c) || c == '\0') { + if (seendigit) /*%< trailing whitespace */ + break; + else { /*%< leading whitespace */ + (*startpp)++; + continue; + } + } + if (c == ';') { + while ((*startpp <= endp) && + ((c = **startpp) != '\n')) + (*startpp)++; + if (seendigit) + break; + continue; + } + if (!isxdigit(c)) { + if (c == ')' && seendigit) { + (*startpp)--; + break; + } + return (-1); + } + (*startpp)++; + if (isdigit(c)) + n = n * 16 + (c - '0'); + else + n = n * 16 + (tolower(c) - 'a' + 10); + seendigit = 1; + } + return (n + m); +} + +/*% + * Get a whitespace delimited base 10 number from a string (not file) into buf + * update the start pointer to point after the number in the string. + */ +static int +getnum_str(u_char **startpp, u_char *endp) { + int c, n; + int seendigit = 0; + int m = 0; + + for (n = 0; *startpp <= endp; ) { + c = **startpp; + if (isspace(c) || c == '\0') { + if (seendigit) /*%< trailing whitespace */ + break; + else { /*%< leading whitespace */ + (*startpp)++; + continue; + } + } + if (c == ';') { + while ((*startpp <= endp) && + ((c = **startpp) != '\n')) + (*startpp)++; + if (seendigit) + break; + continue; + } + if (!isdigit(c)) { + if (c == ')' && seendigit) { + (*startpp)--; + break; + } + return (-1); + } + (*startpp)++; + n = n * 10 + (c - '0'); + seendigit = 1; + } + return (n + m); +} + +/*% + * Allocate a resource record buffer & save rr info. + */ +ns_updrec * +res_mkupdrec(int section, const char *dname, + u_int class, u_int type, u_long ttl) { + ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec)); + + if (!rrecp || !(rrecp->r_dname = strdup(dname))) { + if (rrecp) + free((char *)rrecp); + return (NULL); + } + INIT_LINK(rrecp, r_link); + INIT_LINK(rrecp, r_glink); + rrecp->r_class = (ns_class)class; + rrecp->r_type = (ns_type)type; + rrecp->r_ttl = ttl; + rrecp->r_section = (ns_sect)section; + return (rrecp); +} + +/*% + * Free a resource record buffer created by res_mkupdrec. + */ +void +res_freeupdrec(ns_updrec *rrecp) { + /* Note: freeing r_dp is the caller's responsibility. */ + if (rrecp->r_dname != NULL) + free(rrecp->r_dname); + free(rrecp); +} + +struct valuelist { + struct valuelist * next; + struct valuelist * prev; + char * name; + char * proto; + int port; +}; +static struct valuelist *servicelist, *protolist; + +static void +res_buildservicelist() { + struct servent *sp; + struct valuelist *slp; + +#ifdef MAYBE_HESIOD + setservent(0); +#else + setservent(1); +#endif + while ((sp = getservent()) != NULL) { + slp = (struct valuelist *)malloc(sizeof(struct valuelist)); + if (!slp) + break; + slp->name = strdup(sp->s_name); + slp->proto = strdup(sp->s_proto); + if ((slp->name == NULL) || (slp->proto == NULL)) { + if (slp->name) free(slp->name); + if (slp->proto) free(slp->proto); + free(slp); + break; + } + slp->port = ntohs((u_int16_t)sp->s_port); /*%< host byt order */ + slp->next = servicelist; + slp->prev = NULL; + if (servicelist) + servicelist->prev = slp; + servicelist = slp; + } + endservent(); +} + +void +res_destroyservicelist() { + struct valuelist *slp, *slp_next; + + for (slp = servicelist; slp != NULL; slp = slp_next) { + slp_next = slp->next; + free(slp->name); + free(slp->proto); + free(slp); + } + servicelist = (struct valuelist *)0; +} + +void +res_buildprotolist(void) { + struct protoent *pp; + struct valuelist *slp; + +#ifdef MAYBE_HESIOD + setprotoent(0); +#else + setprotoent(1); +#endif + while ((pp = getprotoent()) != NULL) { + slp = (struct valuelist *)malloc(sizeof(struct valuelist)); + if (!slp) + break; + slp->name = strdup(pp->p_name); + if (slp->name == NULL) { + free(slp); + break; + } + slp->port = pp->p_proto; /*%< host byte order */ + slp->next = protolist; + slp->prev = NULL; + if (protolist) + protolist->prev = slp; + protolist = slp; + } + endprotoent(); +} + +void +res_destroyprotolist(void) { + struct valuelist *plp, *plp_next; + + for (plp = protolist; plp != NULL; plp = plp_next) { + plp_next = plp->next; + free(plp->name); + free(plp); + } + protolist = (struct valuelist *)0; +} + +static int +findservice(const char *s, struct valuelist **list) { + struct valuelist *lp = *list; + int n; + + for (; lp != NULL; lp = lp->next) + if (strcasecmp(lp->name, s) == 0) { + if (lp != *list) { + lp->prev->next = lp->next; + if (lp->next) + lp->next->prev = lp->prev; + (*list)->prev = lp; + lp->next = *list; + *list = lp; + } + return (lp->port); /*%< host byte order */ + } + if (sscanf(s, "%d", &n) != 1 || n <= 0) + n = -1; + return (n); +} + +/*% + * Convert service name or (ascii) number to int. + */ +int +res_servicenumber(const char *p) { + if (servicelist == (struct valuelist *)0) + res_buildservicelist(); + return (findservice(p, &servicelist)); +} + +/*% + * Convert protocol name or (ascii) number to int. + */ +int +res_protocolnumber(const char *p) { + if (protolist == (struct valuelist *)0) + res_buildprotolist(); + return (findservice(p, &protolist)); +} + +static struct servent * +cgetservbyport(u_int16_t port, const char *proto) { /*%< Host byte order. */ + struct valuelist **list = &servicelist; + struct valuelist *lp = *list; + static struct servent serv; + + port = ntohs(port); + for (; lp != NULL; lp = lp->next) { + if (port != (u_int16_t)lp->port) /*%< Host byte order. */ + continue; + if (strcasecmp(lp->proto, proto) == 0) { + if (lp != *list) { + lp->prev->next = lp->next; + if (lp->next) + lp->next->prev = lp->prev; + (*list)->prev = lp; + lp->next = *list; + *list = lp; + } + serv.s_name = lp->name; + serv.s_port = htons((u_int16_t)lp->port); + serv.s_proto = lp->proto; + return (&serv); + } + } + return (0); +} + +static struct protoent * +cgetprotobynumber(int proto) { /*%< Host byte order. */ + struct valuelist **list = &protolist; + struct valuelist *lp = *list; + static struct protoent prot; + + for (; lp != NULL; lp = lp->next) + if (lp->port == proto) { /*%< Host byte order. */ + if (lp != *list) { + lp->prev->next = lp->next; + if (lp->next) + lp->next->prev = lp->prev; + (*list)->prev = lp; + lp->next = *list; + *list = lp; + } + prot.p_name = lp->name; + prot.p_proto = lp->port; /*%< Host byte order. */ + return (&prot); + } + return (0); +} + +const char * +res_protocolname(int num) { + static char number[8]; + struct protoent *pp; + + if (protolist == (struct valuelist *)0) + res_buildprotolist(); + pp = cgetprotobynumber(num); + if (pp == 0) { + (void) sprintf(number, "%d", num); + return (number); + } + return (pp->p_name); +} + +const char * +res_servicename(u_int16_t port, const char *proto) { /*%< Host byte order. */ + static char number[8]; + struct servent *ss; + + if (servicelist == (struct valuelist *)0) + res_buildservicelist(); + ss = cgetservbyport(htons(port), proto); + if (ss == 0) { + (void) sprintf(number, "%d", port); + return (number); + } + return (ss->s_name); +} diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h new file mode 100644 index 0000000000..96c452d89e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef _RES_MKUPDATE_H_ +#define _RES_MKUPDATE_H_ + +__BEGIN_DECLS +__END_DECLS + +#endif /* _RES_MKUPDATE_H_ */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_private.h b/usr/src/lib/libresolv2_joy/common/resolv/res_private.h new file mode 100644 index 0000000000..4e98157ced --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_private.h @@ -0,0 +1,22 @@ +#ifndef res_private_h +#define res_private_h + +struct __res_state_ext { + union res_sockaddr_union nsaddrs[MAXNS]; + struct sort_list { + int af; + union { + struct in_addr ina; + struct in6_addr in6a; + } addr, mask; + } sort_list[MAXRESOLVSORT]; + char nsuffix[64]; + char nsuffix2[64]; +}; + +extern int +res_ourserver_p(const res_state statp, const struct sockaddr *sa); + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_query.c b/usr/src/lib/libresolv2_joy/common/resolv/res_query.c new file mode 100644 index 0000000000..222e4d36ef --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_query.c @@ -0,0 +1,435 @@ +/* + * Portions Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1996-2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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 (c) 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. + */ + +#include "port_before.h" +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "port_after.h" + +/* Options. Leave them on. */ +#define DEBUG + +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif + +/*% + * 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. + */ +int +res_nquery(res_state statp, + const char *name, /*%< domain name */ + int class, int type, /*%< class and type of query */ + u_char *answer, /*%< buffer to put answer */ + int anslen) /*%< size of answer buffer */ +{ + u_char buf[MAXPACKET]; + HEADER *hp = (HEADER *) answer; + u_int oflags; + u_char *rdata; + int n; + + oflags = statp->_flags; + +again: + hp->rcode = NOERROR; /*%< default */ +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_query(%s, %d, %d)\n", name, class, type); +#endif + + n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, + buf, sizeof(buf)); +#ifdef RES_USE_EDNS0 + if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && + (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC|RES_NSID))) { + n = res_nopt(statp, n, buf, sizeof(buf), anslen); + rdata = &buf[n]; + if (n > 0 && (statp->options & RES_NSID) != 0U) { + n = res_nopt_rdata(statp, n, buf, sizeof(buf), rdata, + NS_OPT_NSID, 0, NULL); + } + } +#endif + if (n <= 0) { +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_query: mkquery failed\n"); +#endif + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (n); + } + + n = res_nsend(statp, buf, n, answer, anslen); + if (n < 0) { +#ifdef RES_USE_EDNS0 + /* if the query choked with EDNS0, retry without EDNS0 */ + if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U && + ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) { + statp->_flags |= RES_F_EDNS0ERR; + if (statp->options & RES_DEBUG) + printf(";; res_nquery: retry without EDNS0\n"); + goto again; + } +#endif +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_query: send error\n"); +#endif + RES_SET_H_ERRNO(statp, TRY_AGAIN); + return (n); + } + + if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; rcode = (%s), counts = an:%d ns:%d ar:%d\n", + p_rcode(hp->rcode), + ntohs(hp->ancount), + ntohs(hp->nscount), + ntohs(hp->arcount)); +#endif + switch (hp->rcode) { + case NXDOMAIN: + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); + break; + case SERVFAIL: + RES_SET_H_ERRNO(statp, TRY_AGAIN); + break; + case NOERROR: + RES_SET_H_ERRNO(statp, NO_DATA); + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + RES_SET_H_ERRNO(statp, NO_RECOVERY); + break; + } + return (-1); + } + return (n); +} + +/*% + * 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. + */ +int +res_nsearch(res_state statp, + const char *name, /*%< domain name */ + int class, int type, /*%< class and type of query */ + u_char *answer, /*%< buffer to put answer */ + int anslen) /*%< size of answer */ +{ + const char *cp, * const *domain; + HEADER *hp = (HEADER *) answer; + char tmp[NS_MAXDNAME]; + 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; + + errno = 0; + RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /*%< True if we never query. */ + dots = 0; + for (cp = name; *cp != '\0'; 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(statp, name, tmp, sizeof tmp))!= NULL) + return (res_nquery(statp, cp, class, type, answer, anslen)); + + /* + * 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 >= statp->ndots || trailing_dot) { + ret = res_nquerydomain(statp, name, NULL, class, type, + answer, anslen); + if (ret > 0 || trailing_dot) + return (ret); + saved_herrno = statp->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 && (statp->options & RES_DEFNAMES) != 0U) || + (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0U)) { + int done = 0; + + for (domain = (const char * const *)statp->dnsrch; + *domain && !done; + domain++) { + searched = 1; + + if (domain[0][0] == '\0' || + (domain[0][0] == '.' && domain[0][1] == '\0')) + root_on_list++; + + ret = res_nquerydomain(statp, name, *domain, + class, type, + answer, anslen); + 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(statp, TRY_AGAIN); + return (-1); + } + + switch (statp->res_h_errno) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + if (hp->rcode == SERVFAIL) { + /* try next search element, if any */ + got_servfail++; + 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 ((statp->options & RES_DNSRCH) == 0U) + done++; + } + } + + /* + * 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 || (statp->options & RES_NOTLDQUERY) == 0U) && + !(tried_as_is || root_on_list)) { + ret = res_nquerydomain(statp, name, NULL, class, type, + answer, anslen); + 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. + */ + if (saved_herrno != -1) + RES_SET_H_ERRNO(statp, saved_herrno); + else if (got_nodata) + RES_SET_H_ERRNO(statp, NO_DATA); + else if (got_servfail) + RES_SET_H_ERRNO(statp, 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. + */ +int +res_nquerydomain(res_state statp, + const char *name, + const char *domain, + int class, int type, /*%< class and type of query */ + u_char *answer, /*%< buffer to put answer */ + int anslen) /*%< size of answer */ +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + int n, d; + +#ifdef DEBUG + if (statp->options & RES_DEBUG) + printf(";; res_nquerydomain(%s, %s, %d, %d)\n", + name, domain?domain:"<Nil>", class, type); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name); + if (n >= MAXDNAME) { + RES_SET_H_ERRNO(statp, NO_RECOVERY); + return (-1); + } + n--; + 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(statp, NO_RECOVERY); + return (-1); + } + sprintf(nbuf, "%s.%s", name, domain); + } + return (res_nquery(statp, longname, class, type, answer, anslen)); +} + +const char * +res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) { + char *file, *cp1, *cp2; + char buf[BUFSIZ]; + FILE *fp; + + if (statp->options & RES_NOALIASES) + return (NULL); + file = getenv("HOSTALIASES"); + if (file == NULL || (fp = fopen(file, "r")) == NULL) + return (NULL); + setbuf(fp, NULL); + buf[sizeof(buf) - 1] = '\0'; + while (fgets(buf, sizeof(buf), fp)) { + for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) + ; + if (!*cp1) + break; + *cp1 = '\0'; + if (ns_samename(buf, name) == 1) { + while (isspace((unsigned char)*++cp1)) + ; + if (!*cp1) + break; + for (cp2 = cp1 + 1; *cp2 && + !isspace((unsigned char)*cp2); ++cp2) + ; + *cp2 = '\0'; + strncpy(dst, cp1, siz - 1); + dst[siz - 1] = '\0'; + fclose(fp); + return (dst); + } + } + fclose(fp); + return (NULL); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_send.c b/usr/src/lib/libresolv2_joy/common/resolv/res_send.c new file mode 100644 index 0000000000..764e5c6016 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_send.c @@ -0,0 +1,1115 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Portions Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1996-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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 (c) 1985, 1989, 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. + */ + +/*! \file + * \brief + * Send query to name server and wait for reply. + */ + +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <isc/eventlib.h> + +#include "port_after.h" + +#ifdef USE_POLL +#ifdef HAVE_STROPTS_H +#include <stropts.h> +#endif +#include <poll.h> +#endif /* USE_POLL */ + +/* Options. Leave them on. */ +#define DEBUG +#include "res_debug.h" +#include "res_private.h" + +#define EXT(res) ((res)->_u._ext) + +#ifndef USE_POLL +static const int highestFD = FD_SETSIZE - 1; +#else +static int highestFD = 0; +#endif + +/* Forward. */ + +static int get_salen __P((const struct sockaddr *)); +static struct sockaddr * get_nsaddr __P((res_state, size_t)); +static int send_vc(res_state, const u_char *, int, + u_char *, int, int *, int); +static int send_dg(res_state, const u_char *, int, + u_char *, int, int *, int, int, + int *, int *); +static void Aerror(const res_state, FILE *, const char *, int, + const struct sockaddr *, int); +static void Perror(const res_state, FILE *, const char *, int); +static int sock_eq(struct sockaddr *, struct sockaddr *); +#if defined(NEED_PSELECT) && !defined(USE_POLL) +static int pselect(int, void *, void *, void *, + struct timespec *, + const sigset_t *); +#endif +void res_pquery(const res_state, const u_char *, int, FILE *); + +#ifndef ORIGINAL_ISC_CODE +#pragma weak __res_nameinquery = res_nameinquery +#pragma weak __res_queriesmatch = res_queriesmatch +#pragma weak res_nisourserver = res_ourserver_p +#endif /* ORIGINAL_ISC_CODE */ + +static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; + +/* Public. */ + +/*% + * looks up "ina" in _res.ns_addr_list[] + * + * returns: + *\li 0 : not found + *\li >0 : found + * + * author: + *\li paul vixie, 29may94 + */ +int +res_ourserver_p(const res_state statp, const struct sockaddr *sa) { + const struct sockaddr_in *inp, *srv; + const struct sockaddr_in6 *in6p, *srv6; + int ns; + + switch (sa->sa_family) { + case AF_INET: + inp = (const struct sockaddr_in *)sa; + for (ns = 0; ns < statp->nscount; ns++) { + srv = (struct sockaddr_in *)get_nsaddr(statp, ns); + if (srv->sin_family == inp->sin_family && + srv->sin_port == inp->sin_port && + (srv->sin_addr.s_addr == INADDR_ANY || + srv->sin_addr.s_addr == inp->sin_addr.s_addr)) + return (1); + } + break; + case AF_INET6: + if (EXT(statp).ext == NULL) + break; + in6p = (const struct sockaddr_in6 *)sa; + for (ns = 0; ns < statp->nscount; ns++) { + srv6 = (struct sockaddr_in6 *)get_nsaddr(statp, ns); + if (srv6->sin6_family == in6p->sin6_family && + srv6->sin6_port == in6p->sin6_port && +#ifdef HAVE_SIN6_SCOPE_ID + (srv6->sin6_scope_id == 0 || + srv6->sin6_scope_id == in6p->sin6_scope_id) && +#endif + (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) || + IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr))) + return (1); + } + break; + default: + break; + } + return (0); +} + +/*% + * look for (name,type,class) in the query section of packet (buf,eom) + * + * requires: + *\li buf + HFIXEDSZ <= eom + * + * returns: + *\li -1 : format error + *\li 0 : not found + *\li >0 : found + * + * author: + *\li paul vixie, 29may94 + */ +int +res_nameinquery(const char *name, int type, int class, + const u_char *buf, const u_char *eom) +{ + const u_char *cp = buf + HFIXEDSZ; + int qdcount = ntohs(((const HEADER*)buf)->qdcount); + + while (qdcount-- > 0) { + char tname[MAXDNAME+1]; + int n, ttype, tclass; + + n = dn_expand(buf, eom, cp, tname, sizeof tname); + if (n < 0) + return (-1); + cp += n; + if (cp + 2 * INT16SZ > eom) + return (-1); + ttype = ns_get16(cp); cp += INT16SZ; + tclass = ns_get16(cp); cp += INT16SZ; + if (ttype == type && tclass == class && + ns_samename(tname, name) == 1) + return (1); + } + return (0); +} + +/*% + * is there a 1:1 mapping of (name,type,class) + * in (buf1,eom1) and (buf2,eom2)? + * + * returns: + *\li -1 : format error + *\li 0 : not a 1:1 mapping + *\li >0 : is a 1:1 mapping + * + * author: + *\li paul vixie, 29may94 + */ +int +res_queriesmatch(const u_char *buf1, const u_char *eom1, + const u_char *buf2, const u_char *eom2) +{ + const u_char *cp = buf1 + HFIXEDSZ; + int qdcount = ntohs(((const HEADER*)buf1)->qdcount); + + if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2) + return (-1); + + /* + * Only header section present in replies to + * dynamic update packets. + */ + if ((((const HEADER *)buf1)->opcode == ns_o_update) && + (((const HEADER *)buf2)->opcode == ns_o_update)) + return (1); + + if (qdcount != ntohs(((const HEADER*)buf2)->qdcount)) + return (0); + while (qdcount-- > 0) { + char tname[MAXDNAME+1]; + int n, ttype, tclass; + + n = dn_expand(buf1, eom1, cp, tname, sizeof tname); + if (n < 0) + return (-1); + cp += n; + if (cp + 2 * INT16SZ > eom1) + return (-1); + ttype = ns_get16(cp); cp += INT16SZ; + tclass = ns_get16(cp); cp += INT16SZ; + if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) + return (0); + } + return (1); +} + +int +res_nsend(res_state statp, + const u_char *buf, int buflen, u_char *ans, int anssiz) +{ + int gotsomewhere, terrno, tries, v_circuit, resplen, ns, n; + char abuf[NI_MAXHOST]; + +#ifdef USE_POLL + highestFD = sysconf(_SC_OPEN_MAX) - 1; +#endif + + /* No name servers or res_init() failure */ + if (statp->nscount == 0 || EXT(statp).ext == NULL) { + errno = ESRCH; + return (-1); + } + if (anssiz < HFIXEDSZ) { + errno = EINVAL; + return (-1); + } + DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), + (stdout, ";; res_send()\n"), buf, buflen); + v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; + gotsomewhere = 0; + terrno = ETIMEDOUT; + + /* + * If the ns_addr_list in the resolver context has changed, then + * invalidate our cached copy and the associated timing data. + */ + if (EXT(statp).nscount != 0) { + int needclose = 0; + struct sockaddr_storage peer; + ISC_SOCKLEN_T peerlen; + + if (EXT(statp).nscount != statp->nscount) + needclose++; + else + for (ns = 0; ns < statp->nscount; ns++) { + if (statp->nsaddr_list[ns].sin_family && + !sock_eq((struct sockaddr *)&statp->nsaddr_list[ns], + (struct sockaddr *)&EXT(statp).ext->nsaddrs[ns])) { + needclose++; + break; + } + + if (EXT(statp).nssocks[ns] == -1) + continue; + peerlen = sizeof(peer); + if (getpeername(EXT(statp).nssocks[ns], + (struct sockaddr *)&peer, &peerlen) < 0) { + needclose++; + break; + } + if (!sock_eq((struct sockaddr *)&peer, + get_nsaddr(statp, ns))) { + needclose++; + break; + } + } + if (needclose) { + res_nclose(statp); + EXT(statp).nscount = 0; + } + } + + /* + * Maybe initialize our private copy of the ns_addr_list. + */ + if (EXT(statp).nscount == 0) { + for (ns = 0; ns < statp->nscount; ns++) { + EXT(statp).nstimes[ns] = RES_MAXTIME; + EXT(statp).nssocks[ns] = -1; + if (!statp->nsaddr_list[ns].sin_family) + continue; + EXT(statp).ext->nsaddrs[ns].sin = + statp->nsaddr_list[ns]; + } + EXT(statp).nscount = statp->nscount; + } + + /* + * Some resolvers want to even out the load on their nameservers. + * Note that RES_BLAST overrides RES_ROTATE. + */ + if ((statp->options & RES_ROTATE) != 0U && + (statp->options & RES_BLAST) == 0U) { + union res_sockaddr_union inu; + struct sockaddr_in ina; + int lastns = statp->nscount - 1; + int fd; + u_int16_t nstime; + + if (EXT(statp).ext != NULL) + inu = EXT(statp).ext->nsaddrs[0]; + ina = statp->nsaddr_list[0]; + fd = EXT(statp).nssocks[0]; + nstime = EXT(statp).nstimes[0]; + for (ns = 0; ns < lastns; ns++) { + if (EXT(statp).ext != NULL) + EXT(statp).ext->nsaddrs[ns] = + EXT(statp).ext->nsaddrs[ns + 1]; + statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1]; + EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1]; + EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1]; + } + if (EXT(statp).ext != NULL) + EXT(statp).ext->nsaddrs[lastns] = inu; + statp->nsaddr_list[lastns] = ina; + EXT(statp).nssocks[lastns] = fd; + EXT(statp).nstimes[lastns] = nstime; + } + + /* + * Send request, RETRY times, or until successful. + */ + for (tries = 0; tries < statp->retry; tries++) { + for (ns = 0; ns < statp->nscount; ns++) { + struct sockaddr *nsap; + int nsaplen; + nsap = get_nsaddr(statp, ns); + nsaplen = get_salen(nsap); + statp->_flags &= ~RES_F_LASTMASK; + statp->_flags |= (ns << RES_F_LASTSHIFT); + same_ns: + if (statp->qhook) { + int done = 0, loops = 0; + + do { + res_sendhookact act; + + act = (*statp->qhook)(&nsap, &buf, &buflen, + ans, anssiz, &resplen); + switch (act) { + case res_goahead: + done = 1; + break; + case res_nextns: + res_nclose(statp); + goto next_ns; + case res_done: + return (resplen); + case res_modified: + /* give the hook another try */ + if (++loops < 42) /*doug adams*/ + break; + /*FALLTHROUGH*/ + case res_error: + /*FALLTHROUGH*/ + default: + goto fail; + } + } while (!done); + } + + Dprint(((statp->options & RES_DEBUG) && + getnameinfo(nsap, nsaplen, abuf, sizeof(abuf), + NULL, 0, niflags) == 0), + (stdout, ";; Querying server (# %d) address = %s\n", + ns + 1, abuf)); + + + if (v_circuit) { + /* Use VC; at most one attempt per server. */ + tries = statp->retry; + n = send_vc(statp, buf, buflen, ans, anssiz, &terrno, + ns); + if (n < 0) + goto fail; + if (n == 0) + goto next_ns; + resplen = n; + } else { + /* Use datagrams. */ + n = send_dg(statp, buf, buflen, ans, anssiz, &terrno, + ns, tries, &v_circuit, &gotsomewhere); + if (n < 0) + goto fail; + if (n == 0) + goto next_ns; + if (v_circuit) + goto same_ns; + resplen = n; + } + + Dprint((statp->options & RES_DEBUG) || + ((statp->pfcode & RES_PRF_REPLY) && + (statp->pfcode & RES_PRF_HEAD1)), + (stdout, ";; got answer:\n")); + + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, "%s", ""), + ans, (resplen > anssiz) ? anssiz : resplen); + + /* + * If we have temporarily opened a virtual circuit, + * or if we haven't been asked to keep a socket open, + * close the socket. + */ + if ((v_circuit && (statp->options & RES_USEVC) == 0U) || + (statp->options & RES_STAYOPEN) == 0U) { + res_nclose(statp); + } + if (statp->rhook) { + int done = 0, loops = 0; + + do { + res_sendhookact act; + + act = (*statp->rhook)(nsap, buf, buflen, + ans, anssiz, &resplen); + switch (act) { + case res_goahead: + case res_done: + done = 1; + break; + case res_nextns: + res_nclose(statp); + goto next_ns; + case res_modified: + /* give the hook another try */ + if (++loops < 42) /*doug adams*/ + break; + /*FALLTHROUGH*/ + case res_error: + /*FALLTHROUGH*/ + default: + goto fail; + } + } while (!done); + + } + return (resplen); + next_ns: ; + } /*foreach ns*/ + } /*foreach retry*/ + res_nclose(statp); + if (!v_circuit) { + if (!gotsomewhere) + errno = ECONNREFUSED; /*%< no nameservers found */ + else + errno = ETIMEDOUT; /*%< no answer obtained */ + } else + errno = terrno; + return (-1); + fail: + res_nclose(statp); + return (-1); +} + +/* Private */ + +static int +get_salen(sa) + const struct sockaddr *sa; +{ + +#ifdef HAVE_SA_LEN + /* There are people do not set sa_len. Be forgiving to them. */ + if (sa->sa_len) + return (sa->sa_len); +#endif + + if (sa->sa_family == AF_INET) + return (sizeof(struct sockaddr_in)); + else if (sa->sa_family == AF_INET6) + return (sizeof(struct sockaddr_in6)); + else + return (0); /*%< unknown, die on connect */ +} + +/*% + * pick appropriate nsaddr_list for use. see res_init() for initialization. + */ +static struct sockaddr * +get_nsaddr(statp, n) + res_state statp; + size_t n; +{ + + if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) { + /* + * - EXT(statp).ext->nsaddrs[n] holds an address that is larger + * than struct sockaddr, and + * - user code did not update statp->nsaddr_list[n]. + */ + return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n]; + } else { + /* + * - user code updated statp->nsaddr_list[n], or + * - statp->nsaddr_list[n] has the same content as + * EXT(statp).ext->nsaddrs[n]. + */ + return (struct sockaddr *)(void *)&statp->nsaddr_list[n]; + } +} + +static int +send_vc(res_state statp, + const u_char *buf, int buflen, u_char *ans, int anssiz, + int *terrno, int ns) +{ + const HEADER *hp = (const HEADER *) buf; + HEADER *anhp = (HEADER *) ans; + struct sockaddr *nsap; + int nsaplen; + int truncating, connreset, resplen, n; + struct iovec iov[2]; + u_short len; + u_char *cp; + void *tmp; +#ifdef SO_NOSIGPIPE + int on = 1; +#endif + + nsap = get_nsaddr(statp, ns); + nsaplen = get_salen(nsap); + + connreset = 0; + same_ns: + truncating = 0; + + /* Are we still talking to whom we want to talk to? */ + if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) { + struct sockaddr_storage peer; + ISC_SOCKLEN_T size = sizeof peer; + + if (getpeername(statp->_vcsock, + (struct sockaddr *)&peer, &size) < 0 || + !sock_eq((struct sockaddr *)&peer, nsap)) { + res_nclose(statp); + statp->_flags &= ~RES_F_VC; + } + } + + if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) { + if (statp->_vcsock >= 0) + res_nclose(statp); + + statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0); + if (statp->_vcsock > highestFD) { + res_nclose(statp); + errno = ENOTSOCK; + } + if (statp->_vcsock < 0) { + switch (errno) { + case EPROTONOSUPPORT: +#ifdef EPFNOSUPPORT + case EPFNOSUPPORT: +#endif + case EAFNOSUPPORT: + Perror(statp, stderr, "socket(vc)", errno); + return (0); + default: + *terrno = errno; + Perror(statp, stderr, "socket(vc)", errno); + return (-1); + } + } +#ifdef SO_NOSIGPIPE + /* + * Disable generation of SIGPIPE when writing to a closed + * socket. Write should return -1 and set errno to EPIPE + * instead. + * + * Push on even if setsockopt(SO_NOSIGPIPE) fails. + */ + (void)setsockopt(statp->_vcsock, SOL_SOCKET, SO_NOSIGPIPE, &on, + sizeof(on)); +#endif + errno = 0; + if (connect(statp->_vcsock, nsap, nsaplen) < 0) { + *terrno = errno; + Aerror(statp, stderr, "connect/vc", errno, nsap, + nsaplen); + res_nclose(statp); + return (0); + } + statp->_flags |= RES_F_VC; + } + + /* + * Send length & message + */ + ns_put16((u_short)buflen, (u_char*)&len); + iov[0] = evConsIovec(&len, INT16SZ); + DE_CONST(buf, tmp); + iov[1] = evConsIovec(tmp, buflen); + if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) { + *terrno = errno; + Perror(statp, stderr, "write failed", errno); + res_nclose(statp); + return (0); + } + /* + * Receive length & response + */ + read_len: + cp = ans; + len = INT16SZ; + while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) { + cp += n; + if ((len -= n) == 0) + break; + } + if (n <= 0) { + *terrno = errno; + Perror(statp, stderr, "read failed", errno); + res_nclose(statp); + /* + * A long running process might get its TCP + * connection reset if the remote server was + * restarted. Requery the server instead of + * trying a new one. When there is only one + * server, this means that a query might work + * instead of failing. We only allow one reset + * per query to prevent looping. + */ + if (*terrno == ECONNRESET && !connreset) { + connreset = 1; + res_nclose(statp); + goto same_ns; + } + res_nclose(statp); + return (0); + } + resplen = ns_get16(ans); + if (resplen > anssiz) { + Dprint(statp->options & RES_DEBUG, + (stdout, ";; response truncated\n") + ); + truncating = 1; + len = anssiz; + } else + len = resplen; + if (len < HFIXEDSZ) { + /* + * Undersized message. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; undersized: %d\n", len)); + *terrno = EMSGSIZE; + res_nclose(statp); + return (0); + } + cp = ans; + while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){ + cp += n; + len -= n; + } + if (n <= 0) { + *terrno = errno; + Perror(statp, stderr, "read(vc)", errno); + res_nclose(statp); + return (0); + } + if (truncating) { + /* + * Flush rest of answer so connection stays in synch. + */ + anhp->tc = 1; + len = resplen - anssiz; + while (len != 0) { + char junk[PACKETSZ]; + + n = read(statp->_vcsock, junk, + (len > sizeof junk) ? sizeof junk : len); + if (n > 0) + len -= n; + else + break; + } + } + /* + * If the calling applicating has bailed out of + * a previous call and failed to arrange to have + * the circuit closed or the server has got + * itself confused, then drop the packet and + * wait for the correct one. + */ + if (hp->id != anhp->id) { + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; old answer (unexpected):\n"), + ans, (resplen > anssiz) ? anssiz: resplen); + goto read_len; + } + + /* + * All is well, or the error is fatal. Signal that the + * next nameserver ought not be tried. + */ + return (resplen); +} + +static int +send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans, + int anssiz, int *terrno, int ns, int tries, int *v_circuit, + int *gotsomewhere) +{ + const HEADER *hp = (const HEADER *) buf; + HEADER *anhp = (HEADER *) ans; + const struct sockaddr *nsap; + int nsaplen; + struct timespec now, timeout, finish; + struct sockaddr_storage from; + ISC_SOCKLEN_T fromlen; + int resplen, seconds, n, s; +#ifdef USE_POLL + int polltimeout; + struct pollfd pollfd; +#else + fd_set dsmask; +#endif + + nsap = get_nsaddr(statp, ns); + nsaplen = get_salen(nsap); + if (EXT(statp).nssocks[ns] == -1) { + EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0); + if (EXT(statp).nssocks[ns] > highestFD) { + res_nclose(statp); + errno = ENOTSOCK; + } + if (EXT(statp).nssocks[ns] < 0) { + switch (errno) { + case EPROTONOSUPPORT: +#ifdef EPFNOSUPPORT + case EPFNOSUPPORT: +#endif + case EAFNOSUPPORT: + Perror(statp, stderr, "socket(dg)", errno); + return (0); + default: + *terrno = errno; + Perror(statp, stderr, "socket(dg)", errno); + return (-1); + } + } +#ifndef CANNOT_CONNECT_DGRAM + /* + * On a 4.3BSD+ machine (client and server, + * actually), sending to a nameserver datagram + * port with no nameserver will cause an + * ICMP port unreachable message to be returned. + * If our datagram socket is "connected" to the + * server, we get an ECONNREFUSED error on the next + * socket operation, and select returns if the + * error message is received. We can thus detect + * the absence of a nameserver without timing out. + */ + if (connect(EXT(statp).nssocks[ns], nsap, nsaplen) < 0) { + Aerror(statp, stderr, "connect(dg)", errno, nsap, + nsaplen); + res_nclose(statp); + return (0); + } +#endif /* !CANNOT_CONNECT_DGRAM */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; new DG socket\n")) + } + s = EXT(statp).nssocks[ns]; +#ifndef CANNOT_CONNECT_DGRAM + if (send(s, (const char*)buf, buflen, 0) != buflen) { + Perror(statp, stderr, "send", errno); + res_nclose(statp); + return (0); + } +#else /* !CANNOT_CONNECT_DGRAM */ + if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen) + { + Aerror(statp, stderr, "sendto", errno, nsap, nsaplen); + res_nclose(statp); + return (0); + } +#endif /* !CANNOT_CONNECT_DGRAM */ + + /* + * Wait for reply. + */ + seconds = (statp->retrans << tries); + if (ns > 0) + seconds /= statp->nscount; + if (seconds <= 0) + seconds = 1; + now = evNowTime(); + timeout = evConsTime(seconds, 0); + finish = evAddTime(now, timeout); + goto nonow; + wait: + now = evNowTime(); + nonow: +#ifndef USE_POLL + FD_ZERO(&dsmask); + FD_SET(s, &dsmask); + if (evCmpTime(finish, now) > 0) + timeout = evSubTime(finish, now); + else + timeout = evConsTime(0, 0); + n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL); +#else + timeout = evSubTime(finish, now); + if (timeout.tv_sec < 0) + timeout = evConsTime(0, 0); + polltimeout = 1000*timeout.tv_sec + + timeout.tv_nsec/1000000; + pollfd.fd = s; + pollfd.events = POLLRDNORM; + n = poll(&pollfd, 1, polltimeout); +#endif /* USE_POLL */ + + if (n == 0) { + Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); + *gotsomewhere = 1; + return (0); + } + if (n < 0) { + if (errno == EINTR) + goto wait; +#ifndef USE_POLL + Perror(statp, stderr, "select", errno); +#else + Perror(statp, stderr, "poll", errno); +#endif /* USE_POLL */ + res_nclose(statp); + return (0); + } + errno = 0; + fromlen = sizeof(from); + resplen = recvfrom(s, (char*)ans, anssiz,0, + (struct sockaddr *)&from, &fromlen); + if (resplen <= 0) { + Perror(statp, stderr, "recvfrom", errno); + res_nclose(statp); + return (0); + } + *gotsomewhere = 1; + if (resplen < HFIXEDSZ) { + /* + * Undersized message. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; undersized: %d\n", + resplen)); + *terrno = EMSGSIZE; + res_nclose(statp); + return (0); + } + if (hp->id != anhp->id) { + /* + * response from old query, ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; old answer:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (!(statp->options & RES_INSECURE1) && + !res_ourserver_p(statp, (struct sockaddr *)&from)) { + /* + * response from wrong server? ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; not our server:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } +#ifdef RES_USE_EDNS0 + if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { + /* + * Do not retry if the server do not understand EDNS0. + * The case has to be captured here, as FORMERR packet do not + * carry query section, hence res_queriesmatch() returns 0. + */ + DprintQ(statp->options & RES_DEBUG, + (stdout, "server rejected query with EDNS0:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + /* record the error */ + statp->_flags |= RES_F_EDNS0ERR; + res_nclose(statp); + return (0); + } +#endif + if (!(statp->options & RES_INSECURE2) && + !res_queriesmatch(buf, buf + buflen, + ans, ans + anssiz)) { + /* + * response contains wrong query? ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; wrong query name:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (anhp->rcode == SERVFAIL || + anhp->rcode == NOTIMP || + anhp->rcode == REFUSED) { + DprintQ(statp->options & RES_DEBUG, + (stdout, "server rejected query:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + res_nclose(statp); + /* don't retry if called from dig */ + if (!statp->pfcode) + return (0); + } + if (!(statp->options & RES_IGNTC) && anhp->tc) { + /* + * To get the rest of answer, + * use TCP with same server. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; truncated answer\n")); + *v_circuit = 1; + res_nclose(statp); + return (1); + } + /* + * All is well, or the error is fatal. Signal that the + * next nameserver ought not be tried. + */ + return (resplen); +} + +static void +Aerror(const res_state statp, FILE *file, const char *string, int error, + const struct sockaddr *address, int alen) +{ + int save = errno; + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + + alen = alen; + + if ((statp->options & RES_DEBUG) != 0U) { + if (getnameinfo(address, alen, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), niflags)) { + strncpy(hbuf, "?", sizeof(hbuf) - 1); + hbuf[sizeof(hbuf) - 1] = '\0'; + strncpy(sbuf, "?", sizeof(sbuf) - 1); + sbuf[sizeof(sbuf) - 1] = '\0'; + } + fprintf(file, "res_send: %s ([%s].%s): %s\n", + string, hbuf, sbuf, strerror(error)); + } + errno = save; +} + +static void +Perror(const res_state statp, FILE *file, const char *string, int error) { + int save = errno; + + if ((statp->options & RES_DEBUG) != 0U) + fprintf(file, "res_send: %s: %s\n", + string, strerror(error)); + errno = save; +} + +static int +sock_eq(struct sockaddr *a, struct sockaddr *b) { + struct sockaddr_in *a4, *b4; + struct sockaddr_in6 *a6, *b6; + + if (a->sa_family != b->sa_family) + return 0; + switch (a->sa_family) { + case AF_INET: + a4 = (struct sockaddr_in *)a; + b4 = (struct sockaddr_in *)b; + return a4->sin_port == b4->sin_port && + a4->sin_addr.s_addr == b4->sin_addr.s_addr; + case AF_INET6: + a6 = (struct sockaddr_in6 *)a; + b6 = (struct sockaddr_in6 *)b; + return a6->sin6_port == b6->sin6_port && +#ifdef HAVE_SIN6_SCOPE_ID + a6->sin6_scope_id == b6->sin6_scope_id && +#endif + IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr); + default: + return 0; + } +} + +#if defined(NEED_PSELECT) && !defined(USE_POLL) +/* XXX needs to move to the porting library. */ +static int +pselect(int nfds, void *rfds, void *wfds, void *efds, + struct timespec *tsp, const sigset_t *sigmask) +{ + struct timeval tv, *tvp; + sigset_t sigs; + int n; + + if (tsp) { + tvp = &tv; + tv = evTimeVal(*tsp); + } else + tvp = NULL; + if (sigmask) + sigprocmask(SIG_SETMASK, sigmask, &sigs); + n = select(nfds, rfds, wfds, efds, tvp); + if (sigmask) + sigprocmask(SIG_SETMASK, &sigs, NULL); + if (tsp) + *tsp = evTimeSpec(tv); + return (n); +} +#endif diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c b/usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c new file mode 100644 index 0000000000..5ebc1a70eb --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c @@ -0,0 +1,170 @@ +#include "port_before.h" +#include "fd_setsize.h" + +#include <sys/types.h> +#include <sys/param.h> + +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <isc/dst.h> + +#include <errno.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "port_after.h" + +#define DEBUG +#include "res_debug.h" + + +/*% res_nsendsigned */ +int +res_nsendsigned(res_state statp, const u_char *msg, int msglen, + ns_tsig_key *key, u_char *answer, int anslen) +{ + res_state nstatp; + DST_KEY *dstkey; + int usingTCP = 0; + u_char *newmsg; + int newmsglen, bufsize, siglen; + u_char sig[64]; + HEADER *hp; + time_t tsig_time; + int ret; + int len; + + dst_init(); + + nstatp = (res_state) malloc(sizeof(*statp)); + if (nstatp == NULL) { + errno = ENOMEM; + return (-1); + } + memcpy(nstatp, statp, sizeof(*statp)); + + bufsize = msglen + 1024; + newmsg = (u_char *) malloc(bufsize); + if (newmsg == NULL) { + free(nstatp); + errno = ENOMEM; + return (-1); + } + memcpy(newmsg, msg, msglen); + newmsglen = msglen; + + if (ns_samename(key->alg, NS_TSIG_ALG_HMAC_MD5) != 1) + dstkey = NULL; + else + dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5, + NS_KEY_TYPE_AUTH_ONLY, + NS_KEY_PROT_ANY, + key->data, key->len); + if (dstkey == NULL) { + errno = EINVAL; + free(nstatp); + free(newmsg); + return (-1); + } + + nstatp->nscount = 1; + siglen = sizeof(sig); + ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0, + sig, &siglen, 0); + if (ret < 0) { + free (nstatp); + free (newmsg); + dst_free_key(dstkey); + if (ret == NS_TSIG_ERROR_NO_SPACE) + errno = EMSGSIZE; + else if (ret == -1) + errno = EINVAL; + return (ret); + } + + if (newmsglen > PACKETSZ || nstatp->options & RES_USEVC) + usingTCP = 1; + if (usingTCP == 0) + nstatp->options |= RES_IGNTC; + else + nstatp->options |= RES_USEVC; + /* + * Stop res_send printing the answer. + */ + nstatp->options &= ~RES_DEBUG; + nstatp->pfcode &= ~RES_PRF_REPLY; + +retry: + + len = res_nsend(nstatp, newmsg, newmsglen, answer, anslen); + if (len < 0) { + free (nstatp); + free (newmsg); + dst_free_key(dstkey); + return (len); + } + + ret = ns_verify(answer, &len, dstkey, sig, siglen, + NULL, NULL, &tsig_time, nstatp->options & RES_KEEPTSIG); + if (ret != 0) { + Dprint((statp->options & RES_DEBUG) || + ((statp->pfcode & RES_PRF_REPLY) && + (statp->pfcode & RES_PRF_HEAD1)), + (stdout, ";; got answer:\n")); + + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, "%s", ""), + answer, (anslen > len) ? len : anslen); + + if (ret > 0) { + Dprint(statp->pfcode & RES_PRF_REPLY, + (stdout, ";; server rejected TSIG (%s)\n", + p_rcode(ret))); + } else { + Dprint(statp->pfcode & RES_PRF_REPLY, + (stdout, ";; TSIG invalid (%s)\n", + p_rcode(-ret))); + } + + free (nstatp); + free (newmsg); + dst_free_key(dstkey); + if (ret == -1) + errno = EINVAL; + else + errno = ENOTTY; + return (-1); + } + + hp = (HEADER *) answer; + if (hp->tc && !usingTCP && (statp->options & RES_IGNTC) == 0U) { + nstatp->options &= ~RES_IGNTC; + usingTCP = 1; + goto retry; + } + Dprint((statp->options & RES_DEBUG) || + ((statp->pfcode & RES_PRF_REPLY) && + (statp->pfcode & RES_PRF_HEAD1)), + (stdout, ";; got answer:\n")); + + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, "%s", ""), + answer, (anslen > len) ? len : anslen); + + Dprint(statp->pfcode & RES_PRF_REPLY, (stdout, ";; TSIG ok\n")); + + free (nstatp); + free (newmsg); + dst_free_key(dstkey); + return (len); +} + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_update.c b/usr/src/lib/libresolv2_joy/common/resolv/res_update.c new file mode 100644 index 0000000000..bf95aef48d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_update.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief + * Based on the Dynamic DNS reference implementation by Viraj Bais + * <viraj_bais@ccm.fm.intel.com> + */ + +#include "port_before.h" + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <errno.h> +#include <limits.h> +#include <netdb.h> +#include <res_update.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/list.h> +#include <resolv_joy.h> + +#include "port_after.h" +#include "res_private.h" + +/*% + * Separate a linked list of records into groups so that all records + * in a group will belong to a single zone on the nameserver. + * Create a dynamic update packet for each zone and send it to the + * nameservers for that zone, and await answer. + * Abort if error occurs in updating any zone. + * Return the number of zones updated on success, < 0 on error. + * + * On error, caller must deal with the unsynchronized zones + * eg. an A record might have been successfully added to the forward + * zone but the corresponding PTR record would be missing if error + * was encountered while updating the reverse zone. + */ + +struct zonegrp { + char z_origin[MAXDNAME]; + ns_class z_class; + union res_sockaddr_union z_nsaddrs[MAXNS]; + int z_nscount; + int z_flags; + LIST(ns_updrec) z_rrlist; + LINK(struct zonegrp) z_link; +}; + +#define ZG_F_ZONESECTADDED 0x0001 + +/* Forward. */ + +static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2); + +/* Macros. */ + +#define DPRINTF(x) do {\ + int save_errno = errno; \ + if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \ + errno = save_errno; \ + } while (0) + +/* Public. */ + +int +res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) { + ns_updrec *rrecp; + u_char answer[PACKETSZ]; + u_char *packet; + struct zonegrp *zptr, tgrp; + LIST(struct zonegrp) zgrps; + int nzones = 0, nscount = 0, n; + union res_sockaddr_union nsaddrs[MAXNS]; + + packet = malloc(NS_MAXMSG); + if (packet == NULL) { + DPRINTF(("malloc failed")); + return (0); + } + /* Thread all of the updates onto a list of groups. */ + INIT_LIST(zgrps); + memset(&tgrp, 0, sizeof (tgrp)); + for (rrecp = rrecp_in; rrecp; + rrecp = LINKED(rrecp, r_link) ? NEXT(rrecp, r_link) : NULL) { + int nscnt; + /* Find the origin for it if there is one. */ + tgrp.z_class = rrecp->r_class; + nscnt = res_findzonecut2(statp, rrecp->r_dname, tgrp.z_class, + RES_EXHAUSTIVE, tgrp.z_origin, + sizeof tgrp.z_origin, + tgrp.z_nsaddrs, MAXNS); + if (nscnt <= 0) { + DPRINTF(("res_findzonecut failed (%d)", nscnt)); + goto done; + } + tgrp.z_nscount = nscnt; + /* Find the group for it if there is one. */ + for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) + if (ns_samename(tgrp.z_origin, zptr->z_origin) == 1 && + tgrp.z_class == zptr->z_class) + break; + /* Make a group for it if there isn't one. */ + if (zptr == NULL) { + zptr = malloc(sizeof *zptr); + if (zptr == NULL) { + DPRINTF(("malloc failed")); + goto done; + } + *zptr = tgrp; + zptr->z_flags = 0; + INIT_LINK(zptr, z_link); + INIT_LIST(zptr->z_rrlist); + APPEND(zgrps, zptr, z_link); + } + /* Thread this rrecp onto the right group. */ + APPEND(zptr->z_rrlist, rrecp, r_glink); + } + + for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) { + /* Construct zone section and prepend it. */ + rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin, + zptr->z_class, ns_t_soa, 0); + if (rrecp == NULL) { + DPRINTF(("res_mkupdrec failed")); + goto done; + } + PREPEND(zptr->z_rrlist, rrecp, r_glink); + zptr->z_flags |= ZG_F_ZONESECTADDED; + + /* Marshall the update message. */ + n = res_nmkupdate(statp, HEAD(zptr->z_rrlist), + packet, NS_MAXMSG); + DPRINTF(("res_mkupdate -> %d", n)); + if (n < 0) + goto done; + + /* Temporarily replace the resolver's nameserver set. */ + nscount = res_getservers(statp, nsaddrs, MAXNS); + res_setservers(statp, zptr->z_nsaddrs, zptr->z_nscount); + + /* Send the update and remember the result. */ + if (key != NULL) + n = res_nsendsigned(statp, packet, n, key, + answer, sizeof answer); + else + n = res_nsend(statp, packet, n, answer, sizeof answer); + if (n < 0) { + DPRINTF(("res_nsend: send error, n=%d (%s)\n", + n, strerror(errno))); + goto done; + } + if (((HEADER *)answer)->rcode == NOERROR) + nzones++; + + /* Restore resolver's nameserver set. */ + res_setservers(statp, nsaddrs, nscount); + nscount = 0; + } + done: + while (!EMPTY(zgrps)) { + zptr = HEAD(zgrps); + if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0) + res_freeupdrec(HEAD(zptr->z_rrlist)); + UNLINK(zgrps, zptr, z_link); + free(zptr); + } + if (nscount != 0) + res_setservers(statp, nsaddrs, nscount); + + free(packet); + return (nzones); +} + +/* Private. */ + +static void +res_dprintf(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + fputs(";; res_nupdate: ", stderr); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); +} diff --git a/usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c b/usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c new file mode 100644 index 0000000000..cc2a485ede --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c @@ -0,0 +1,126 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <port_before.h> +#include <thread.h> +#include <errno.h> +#include <netdb.h> +#include <malloc.h> +#include <string.h> +#include <resolv_mt.h> +#include <irs.h> +#include <port_after.h> + +#pragma redefine_extname __h_errno __joy_h_errno + +/* + * much of the original version of sunw_mtxtxres.c was incorporated into + * ISC libbind as resolv/mtctxres.c. The following bits have not yet made + * it into ISC libbind. + */ + +/* + * There used to be a private, MT-safe resolver interface that used TSD + * to store per-thread _res, h_errno, etc. We continue to provide the + * access functions __res_get_res() and __res_get_h_errno() so that binaries + * that used the private interface will continue to work. + */ + +#ifdef _res +#undef _res +#endif + +extern struct __res_state *__res_state(void); + +struct __res_state * +__res_get_res(void) { + return (__res_state()); +} + + +#ifdef h_errno +#undef h_errno +#endif + +extern int *__h_errno(void); + +int * +__res_get_h_errno(void) { + return (__h_errno()); +} + + +#ifdef SUNW_HOSTS_FALLBACK + +/* + * When the name service switch calls libresolv, it doesn't want fallback + * to /etc/hosts, so we provide a method to turn it off. + */ + +void +__joy_res_set_no_hosts_fallback(void) { + ___mtctxres()->no_hosts_fallback_private = 1; +} + +void +__joy_res_unset_no_hosts_fallback(void) { + ___mtctxres()->no_hosts_fallback_private = 0; +} + +int +__res_no_hosts_fallback(void) { + return (___mtctxres()->no_hosts_fallback_private); +} + +#endif /* SUNW_HOSTS_FALLBACK */ + +#ifdef SUNW_OVERRIDE_RETRY + +/* + * The NS switch wants to be able to override the number of retries. + */ + +int +__joy_res_override_retry(int retry) { + ___mtctxres()->retry_private = retry; + /* + * This function doesn't really need a return value; saving the + * old retry setting, and restoring it, is handled by __res_retry() + * and __res_retry_reset() below. However, the nss_dns library + * must have a private version of this function to be used when + * running with an old libresolv. That private nss_dns function + * needs a return value, and a function pointer is used to select + * the right function at runtime. Thus, __res_override_retry + * must have a function prototype consistent with the private + * nss_dns function, i.e., one that returns an int. + * + * Given that we do have a return value, that value must be zero. + * That's because retry_private == 0 is used to indicate that + * no override retry value is in effect, and the way we expect + * nss_dns to call us is: + * + * int oldretry = __res_override_retry(N); + * <whatever> + * (void)__res_override_retry(old_retry); + */ + return (0); +} + +int +__res_retry(int retry) { + mtctxres_t *mt = ___mtctxres(); + + mt->retry_save = retry; + return ((mt->retry_private != 0) ? mt->retry_private : retry); +} + +int +__res_retry_reset(void) { + mtctxres_t *mt = ___mtctxres(); + + return (mt->retry_save); +} + +#endif /* SUNW_OVERRIDE_RETRY */ diff --git a/usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c b/usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c new file mode 100644 index 0000000000..3b0ffc49df --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c @@ -0,0 +1,249 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * As of BIND 8.2.2, ISC (a) removed res_mkupdate(), res_update(), and + * res_mkupdrec() from what they consider the supported interface. The + * functions still exist, but their calling interface has changed, since + * the ns_updrec structure has changed. + * + * It seems probable that res_mkupdate() etc. will return, though possibly + * with other changes, in some future BIND release. In order to avoid + * going to PSARC twice (once to remove the functions, and then again to + * add them back), we retain the old interface as a wrapper around the + * new one. + */ + +#include <port_before.h> + +#include <malloc.h> +#include <strings.h> +#include <sys/types.h> +#include <netinet/in.h> + +/* get the Solaris ns_updrec before any renaming happens */ +#include <arpa/nameser.h> + +/* get the __ISC_ns_updrec */ +#include <res_update.h> + +#include <port_after.h> + +/* un-rename ns_updrec and res_* functions so we can wrap them */ +#undef ns_updrec +#undef res_mkupdate +#undef res_update +#undef res_mkupdrec +#undef res_freeupdrec +#undef res_nmkupdate +#undef res_nupdate + +void res_freeupdrec(ns_updrec *); + +static int +old2new(ns_updrec *old, __ISC_ns_updrec *new) { + + if (old->r_dname != 0) { + if ((new->r_dname = strdup(old->r_dname)) == 0) + return (-1); + } else { + new->r_dname = 0; + } + + new->r_glink.prev = + new->r_glink.next = + new->r_link.prev = + new->r_link.next = 0; + + new->r_section = old->r_section; + new->r_class = old->r_class; + new->r_type = old->r_type; + new->r_ttl = old->r_ttl; + new->r_data = old->r_data; + new->r_size = old->r_size; + new->r_opcode = old->r_opcode; + new->r_dp = old->r_dp; + new->r_deldp = old->r_deldp; + new->r_zone = old->r_zone; + + return (0); +} + + +static int +new2old(__ISC_ns_updrec *new, ns_updrec *old) { + /* XXX r_prev and r_next unchanged */ + if (new->r_dname != 0) { + if ((old->r_dname = strdup(new->r_dname)) == 0) + return (-1); + } else { + old->r_dname = 0; + } + old->r_section = new->r_section; + old->r_class = new->r_class; + old->r_type = new->r_type; + old->r_ttl = new->r_ttl; + old->r_data = new->r_data; + old->r_size = new->r_size; + old->r_opcode = new->r_opcode; + old->r_grpnext = 0; /* XXX */ + old->r_dp = new->r_dp; + old->r_deldp = new->r_deldp; + old->r_zone = new->r_zone; + + return (0); +} + + +static void +delete_list(__ISC_ns_updrec *list) { + + __ISC_ns_updrec *next; + + for (; list != 0; list = next) { + next = list->r_link.next; + __ISC_res_freeupdrec(list); + } +} + + +static __ISC_ns_updrec * +copy_list(ns_updrec *old, int do_glink) { + + __ISC_ns_updrec *list = 0, *r, *p; + + if (old == 0) + return (0); + + for (p = 0; old != 0; old = old->r_next, p = r) { + if ((r = calloc(1, sizeof (*r))) == 0 || + old2new(old, r) != 0) { + free(r); + delete_list(list); + return (0); + } + r->r_link.prev = p; + r->r_link.next = 0; + /* res_update and res_nupdate want r_glink set up like this */ + if (do_glink) { + r->r_glink.prev = p; + r->r_glink.next = 0; + } else { + r->r_glink.prev = (void *)-1; + r->r_glink.next = (void *)-1; + } + if (p != 0) { + p->r_link.next = r; + if (do_glink) { + p->r_glink.next = r; + } + } else { + list = r; + } + } + return (list); +} + + +int +res_mkupdate(ns_updrec *rrecp_in, uchar_t *buf, int length) { + + __ISC_ns_updrec *r; + int ret; + + if ((r = copy_list(rrecp_in, 1)) == 0) + return (-1); + + ret = __ISC_res_mkupdate(r, buf, length); + + delete_list(r); + + return (ret); +} + +int +res_nmkupdate(res_state statp, ns_updrec *rrecp_in, uchar_t *buf, int length) { + + __ISC_ns_updrec *r; + int ret; + + if ((r = copy_list(rrecp_in, 1)) == 0) + return (-1); + + ret = __ISC_res_nmkupdate(statp, r, buf, length); + + delete_list(r); + + return (ret); +} + + +int +res_update(ns_updrec *rrecp_in) { + + __ISC_ns_updrec *r; + int ret; + + if ((r = copy_list(rrecp_in, 0)) == 0) + return (-1); + + ret = __ISC_res_update(r); + + delete_list(r); + + return (ret); +} + +int +res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) { + + __ISC_ns_updrec *r; + int ret; + + if ((r = copy_list(rrecp_in, 0)) == 0) + return (-1); + + ret = __ISC_res_nupdate(statp, r, key); + + delete_list(r); + + return (ret); +} + + + +ns_updrec * +res_mkupdrec(int section, const char *dname, uint_t class, uint_t type, + uint_t ttl) { + + __ISC_ns_updrec *n; + ns_updrec *o; + + n = __ISC_res_mkupdrec(section, dname, class, type, ttl); + if (n == 0) + return (0); + + if ((o = calloc(1, sizeof (*o))) != 0) { + if (new2old(n, o) != 0) { + res_freeupdrec(o); + o = 0; + } + } + + __ISC_res_freeupdrec(n); + + return (o); +} + + +void +res_freeupdrec(ns_updrec *rrecp) { + if (rrecp == 0) + return; + /* Note: freeing r_dp is the caller's responsibility. */ + if (rrecp->r_dname != NULL) + free(rrecp->r_dname); + free(rrecp); +} diff --git a/usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c b/usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c new file mode 100644 index 0000000000..55bbe07024 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c @@ -0,0 +1,23 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <port_before.h> +#include <resolv_joy.h> +#include <arpa/inet.h> +#include <port_after.h> + +#undef p_option +/* extern const char * isc_p_option(); */ +const char *p_option(uint_t option) { + return (isc_p_option((ulong_t)option)); +} +#pragma weak __p_option = p_option + +#undef p_secstodate +/* extern char * isc_p_secstodate (); */ +char *p_secstodate(uint_t secs) { + return (isc_p_secstodate((ulong_t)secs)); +} +#pragma weak __p_secstodate = p_secstodate diff --git a/usr/src/lib/libresolv2_joy/i386/Makefile b/usr/src/lib/libresolv2_joy/i386/Makefile new file mode 100644 index 0000000000..a333224278 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libresolv2_joy/include/Makefile b/usr/src/lib/libresolv2_joy/include/Makefile new file mode 100644 index 0000000000..8bff3c3188 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/Makefile @@ -0,0 +1,60 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../../../Makefile.master + +HDRS= os_version.h port_ipv6.h +TMPHDRS= new_os_version.h new_port_ipv6.h + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install + +.KEEP_STATE: + +all lint: $(HDRS) + +install: all + +clean: + $(RM) $(HDRS) $(TMPHDRS) + +clobber: clean + +# os_version.h and port_ipv6.h should be rebuilt when you change OS +# revision. Since that's not easily expressed as a dependency, we +# rebuild them every time. + +os_version.h: make_os_version FRC + ./make_os_version + +port_ipv6.h: probe_ipv6 FRC + CC="$(CC)" ./probe_ipv6 + +FRC: diff --git a/usr/src/lib/libresolv2_joy/include/arpa/port_inet.h b/usr/src/lib/libresolv2_joy/include/arpa/port_inet.h new file mode 100644 index 0000000000..5eb1787f56 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/arpa/port_inet.h @@ -0,0 +1,41 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * + * All rights reserved. + */ + +#ifndef _ARPA_PORT_INET_H +#define _ARPA_PORT_INET_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * these are libresolv2 functions that were made local in previous versions + * we rename them res_* because they conflict with libnsl or libsocket + */ + +#define inet_lnaof res_inet_lnaof /* libsocket */ +ulong_t inet_lnaof(struct in_addr in); + +#define inet_makeaddr res_inet_makeaddr /* libsocket */ +struct in_addr inet_makeaddr(ulong_t net, ulong_t host); + +#define inet_netof res_inet_netof /* libnsl */ +ulong_t inet_netof(struct in_addr in); + +#define inet_network res_inet_network /* libsocket */ +ulong_t inet_network(register const char *cp); + +#ifdef __cplusplus +} +#endif + + + +#endif /* _ARPA_PORT_INET_H */ diff --git a/usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h b/usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h new file mode 100644 index 0000000000..b40ea0d163 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h @@ -0,0 +1,40 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ARPA_PORT_NAMESER_H +#define _ARPA_PORT_NAMESER_H + +/* + * ISC changed the ns_updrec structure. However, it's a public interface + * in Solaris, so we rename it here and wrap in sunw_updrec.c + */ +#define ns_updrec __ISC_ns_updrec + + +/* + * Due to the above, the following functions need to be renamed and + * wrapped in sunw_updrec.c. + * + * For BIND 8.2.2, ISC removed the dynamic update functions, and the + * definition of the ns_updrec structure, from the public include files + * (<resolv.h>, <arpa/nameser.h>. However, res_update(), res_mkupdate(), + * and res_mkupdrec() are in the public libresolv interface in Solaris, + * so we can't easily remove them. Thus, ISC's new versions of res_mkupdate() + * etc. can't be exposed under their original names. + * + * res_nmkupdate() and res_nupdate are new. We could either change them + * to accept the <arpa/nameser.h> ns_updrec, or leave them unchanged and + * undocumented. Since ISC may change ns_updrec again, we pick the latter + * solution for now. + */ +#define res_mkupdate __ISC_res_mkupdate +#define res_update __ISC_res_update +#define res_mkupdrec __ISC_res_mkupdrec +#define res_freeupdrec __ISC_res_freeupdrec +#define res_nmkupdate __ISC_res_nmkupdate +#define res_nupdate __ISC_res_nupdate + + +#endif /* _ARPA_PORT_NAMESER_H */ diff --git a/usr/src/lib/libresolv2_joy/include/conf/sunoptions.h b/usr/src/lib/libresolv2_joy/include/conf/sunoptions.h new file mode 100644 index 0000000000..b75ff9d878 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/conf/sunoptions.h @@ -0,0 +1,30 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SUNOPTIONS_H +#define _SUNOPTIONS_H + +#define USELOOPBACK /* Resolver library defaults to 127.0.0.1 */ + +/* Additions for Solaris 2 */ + +#define SUNW_INITCHKIF /* Check if any non-loopback interface is up */ +#define SUNW_CONFCHECK /* Abort quickly if no /etc/resolv.conf or */ + /* local named */ +#define SUNW_HOSTS_FALLBACK /* Configurable /etc/hosts fallback */ +#define SUNW_HNOK_UNDERSCORE /* Allow underscore in hostnames (libresolv) */ +#define SUNW_MT_RESOLVER /* MT hot extensions (libresolv) */ +#define SUNW_SETHERRNO /* ISC does not set h_errno in gethostbyname */ +#define SUNW_OVERRIDE_RETRY /* Allow NS switch to override res->retry */ +#define SUNW_LIBMD5 /* Use md5(3EXT) instead of internal implementation */ + +/* If compiling an MT warm libresolv, we also need reentrancy */ +#if defined(SUNW_MT_RESOLVER) && !defined(_REENTRANT) +#define _REENTRANT +#endif + +/* End additions for Solaris 2 */ + +#endif /* _SUNOPTIONS_H */ diff --git a/usr/src/lib/libresolv2_joy/include/config.h b/usr/src/lib/libresolv2_joy/include/config.h new file mode 100644 index 0000000000..35fb115a0f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/config.h @@ -0,0 +1,75 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* config.h. Generated from config.h.in by configure. */ +/* #undef _SOCKADDR_LEN */ +#define HAVE_FCNTL_H 1 +/* #undef HAVE_PATHS_H */ +#define HAVE_INTTYPES_H 1 +#define HAVE_STROPTS_H 1 +/* #undef HAVE_SYS_TIMERS_H */ +#define HAVE_SYS_SELECT_H 1 +#define HAVE_MEMORY_H 1 +/* #undef SYS_CDEFS_H */ +#define _POSIX_PTHREAD_SEMANTICS 1 +#define POSIX_GETPWUID_R 1 +#define POSIX_GETPWNAM_R 1 +#define POSIX_GETGRGID_R 1 +#define POSIX_GETGRNAM_R 1 +#define HAVE_MEMMOVE 1 +#define HAVE_MEMCHR 1 +/* #undef SPRINTF_CHAR */ +/* #undef VSPRINTF_CHAR */ +#define USE_SYSERROR_LIST 1 +/* #undef NEED_STRTOUL */ +/* #undef NEED_SUN4PROTOS */ +/* #undef REENABLE_SEND */ + +#define NEED_SETGROUPENT 1 +#define NEED_GETGROUPLIST 1 + +/* define if prototype for getgrnam_r() is required */ +/* #undef NEED_GETGRNAM_R */ +/* #undef NEED_GETGRGID_R */ +/* #undef NEED_GETGRENT_R */ +#define NEED_SETGRENT_R 1 +#define NEED_ENDGRENT_R 1 + +#define NEED_INNETGR_R 1 +/* #undef NEED_SETNETGRENT_R */ +#define NEED_ENDNETGRENT_R 1 + +/* #undef NEED_GETPWNAM_R */ +/* #undef NEED_GETPWUID_R */ +#define NEED_SETPWENT_R 1 +#define NEED_SETPASSENT_R 1 +#define NEED_SETPWENT_R 1 +/* #undef NEED_GETPWENT_R */ +#define NEED_ENDPWENT_R 1 + +#define NEED_SETPASSENT 1 + +/* #undef HAS_PW_CLASS */ + +/* #undef ssize_t */ +/* #undef uintptr_t */ + +/* Shut up warnings about sputaux in stdio.h on BSD/OS pre-4.1 */ +/* #undef SHUTUP_SPUTAUX */ +#ifdef SHUTUP_SPUTAUX +struct __sFILE; +extern __inline int __sputaux(int _c, struct __sFILE *_p); +#endif +#define BROKEN_IN6ADDR_INIT_MACROS 1 +#define HAVE_STRLCAT 1 +/* Shut up warnings about missing braces */ +/* #undef SHUTUP_MUTEX_INITIALIZER */ +#ifdef SHUTUP_MUTEX_INITIALIZER +#define LIBBIND_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } +#else +#define LIBBIND_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif + diff --git a/usr/src/lib/libresolv2_joy/include/err.h b/usr/src/lib/libresolv2_joy/include/err.h new file mode 100644 index 0000000000..45992ea336 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/err.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/*- + * Copyright (c) 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. + * + * @(#)err.h 8.1 (Berkeley) 6/2/93 + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _ERR_H_ +#define _ERR_H_ + +#include <sys/cdefs.h> +#include <stdarg.h> + +__BEGIN_DECLS +__dead void err __P((int, const char *, ...)) __attribute__((__volatile)); +__dead void verr __P((int, const char *, va_list)) + __attribute__((__volatile)); +__dead void errx __P((int, const char *, ...)) __attribute__((__volatile)); +__dead void verrx __P((int, const char *, va_list)) + __attribute__((__volatile)); +void warn __P((const char *, ...)); +void vwarn __P((const char *, va_list)); +void warnx __P((const char *, ...)); +void vwarnx __P((const char *, va_list)); +__END_DECLS + +#endif /* !_ERR_H_ */ diff --git a/usr/src/lib/libresolv2_joy/include/fd_setsize.h b/usr/src/lib/libresolv2_joy/include/fd_setsize.h new file mode 100644 index 0000000000..0e21049742 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/fd_setsize.h @@ -0,0 +1,10 @@ +#ifndef _FD_SETSIZE_H +#define _FD_SETSIZE_H + +/*% + * If you need a bigger FD_SETSIZE, this is NOT the place to set it. + * This file is a fallback for BIND ports which don't specify their own. + */ + +#endif /* _FD_SETSIZE_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/hesiod.h b/usr/src/lib/libresolv2_joy/include/hesiod.h new file mode 100644 index 0000000000..d64c0c5e80 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/hesiod.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/*! \file + * \brief + * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>. + */ + +/* + * $Id: hesiod.h,v 1.4 2005/04/27 04:56:14 sra Exp $ + */ + +#ifndef _HESIOD_H_INCLUDED +#define _HESIOD_H_INCLUDED + +int hesiod_init __P((void **)); +void hesiod_end __P((void *)); +char * hesiod_to_bind __P((void *, const char *, const char *)); +char ** hesiod_resolve __P((void *, const char *, const char *)); +void hesiod_free_list __P((void *, char **)); +struct __res_state * __hesiod_res_get __P((void *)); +void __hesiod_res_set __P((void *, struct __res_state *, + void (*)(void *))); + +#endif /*_HESIOD_H_INCLUDED*/ diff --git a/usr/src/lib/libresolv2_joy/include/irp.h b/usr/src/lib/libresolv2_joy/include/irp.h new file mode 100644 index 0000000000..1290bd068f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/irp.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: irp.h,v 1.4 2005/04/27 04:56:15 sra Exp $ + */ + +#ifndef _IRP_H_INCLUDED +#define _IRP_H_INCLUDED + +/*! \file */ + +#define IRPD_TIMEOUT 30 /*%< seconds */ +#define IRPD_MAXSESS 50 /*%< number of simultaneous sessions. */ +#define IRPD_PORT 6660 /*%< 10 times the number of the beast. */ +#define IRPD_PATH "/var/run/irpd" /*%< af_unix socket path */ + +/* If sets the environment variable IRPDSERVER to an IP address + (e.g. "192.5.5.1"), then that's the host the client expects irpd to be + running on. */ +#define IRPD_HOST_ENV "IRPDSERVER" + +/* Protocol response codes. */ +#define IRPD_WELCOME_CODE 200 +#define IRPD_NOT_WELCOME_CODE 500 + +#define IRPD_GETHOST_ERROR 510 +#define IRPD_GETHOST_NONE 210 +#define IRPD_GETHOST_OK 211 +#define IRPD_GETHOST_SETOK 212 + +#define IRPD_GETNET_ERROR 520 +#define IRPD_GETNET_NONE 220 +#define IRPD_GETNET_OK 221 +#define IRPD_GETNET_SETOK 222 + +#define IRPD_GETUSER_ERROR 530 +#define IRPD_GETUSER_NONE 230 +#define IRPD_GETUSER_OK 231 +#define IRPD_GETUSER_SETOK 232 + +#define IRPD_GETGROUP_ERROR 540 +#define IRPD_GETGROUP_NONE 240 +#define IRPD_GETGROUP_OK 241 +#define IRPD_GETGROUP_SETOK 242 + +#define IRPD_GETSERVICE_ERROR 550 +#define IRPD_GETSERVICE_NONE 250 +#define IRPD_GETSERVICE_OK 251 +#define IRPD_GETSERVICE_SETOK 252 + +#define IRPD_GETPROTO_ERROR 560 +#define IRPD_GETPROTO_NONE 260 +#define IRPD_GETPROTO_OK 261 +#define IRPD_GETPROTO_SETOK 262 + +#define IRPD_GETNETGR_ERROR 570 +#define IRPD_GETNETGR_NONE 270 +#define IRPD_GETNETGR_OK 271 +#define IRPD_GETNETGR_NOMORE 272 +#define IRPD_GETNETGR_MATCHES 273 +#define IRPD_GETNETGR_NOMATCH 274 +#define IRPD_GETNETGR_SETOK 275 +#define IRPD_GETNETGR_SETERR 276 + +#define irs_irp_read_body __irs_irp_read_body +#define irs_irp_read_response __irs_irp_read_response +#define irs_irp_disconnect __irs_irp_disconnect +#define irs_irp_connect __irs_irp_connect +#define irs_irp_connection_setup __irs_irp_connection_setup +#define irs_irp_send_command __irs_irp_send_command + +struct irp_p; + +char *irs_irp_read_body(struct irp_p *, size_t *); +int irs_irp_read_response(struct irp_p *, char *, size_t); +void irs_irp_disconnect(struct irp_p *); +int irs_irp_connect(struct irp_p *); +int irs_irp_is_connected(struct irp_p *); +int irs_irp_connection_setup(struct irp_p *, int *); +#ifdef __GNUC__ +int irs_irp_send_command(struct irp_p *, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +#else +int irs_irp_send_command(struct irp_p *, const char *, ...); +#endif +int irs_irp_get_full_response(struct irp_p *, int *, char *, size_t, + char **, size_t *); +int irs_irp_read_line(struct irp_p *, char *, int); + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/irs.h b/usr/src/lib/libresolv2_joy/include/irs.h new file mode 100644 index 0000000000..386e3cb3f6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/irs.h @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: irs.h,v 1.5 2005/04/27 04:56:15 sra Exp $ + */ + +#ifndef _IRS_H_INCLUDED +#define _IRS_H_INCLUDED + +/*! \file */ + +#include <sys/types.h> + +#include <arpa/nameser.h> + +#include <grp.h> +#include <netdb.h> +#include <resolv_joy.h> +#include <pwd.h> + +/*% + * This is the group map class. + */ +struct irs_gr { + void * private; + void (*close) __P((struct irs_gr *)); + struct group * (*next) __P((struct irs_gr *)); + struct group * (*byname) __P((struct irs_gr *, const char *)); + struct group * (*bygid) __P((struct irs_gr *, gid_t)); + int (*list) __P((struct irs_gr *, const char *, + gid_t, gid_t *, int *)); + void (*rewind) __P((struct irs_gr *)); + void (*minimize) __P((struct irs_gr *)); + struct __res_state * (*res_get) __P((struct irs_gr *)); + void (*res_set) __P((struct irs_gr *, res_state, + void (*)(void *))); +}; + +/*% + * This is the password map class. + */ +struct irs_pw { + void * private; + void (*close) __P((struct irs_pw *)); + struct passwd * (*next) __P((struct irs_pw *)); + struct passwd * (*byname) __P((struct irs_pw *, const char *)); + struct passwd * (*byuid) __P((struct irs_pw *, uid_t)); + void (*rewind) __P((struct irs_pw *)); + void (*minimize) __P((struct irs_pw *)); + struct __res_state * (*res_get) __P((struct irs_pw *)); + void (*res_set) __P((struct irs_pw *, res_state, + void (*)(void *))); +}; + +/*% + * This is the service map class. + */ +struct irs_sv { + void * private; + void (*close) __P((struct irs_sv *)); + struct servent *(*byname) __P((struct irs_sv *, + const char *, const char *)); + struct servent *(*byport) __P((struct irs_sv *, int, const char *)); + struct servent *(*next) __P((struct irs_sv *)); + void (*rewind) __P((struct irs_sv *)); + void (*minimize) __P((struct irs_sv *)); + struct __res_state * (*res_get) __P((struct irs_sv *)); + void (*res_set) __P((struct irs_sv *, res_state, + void (*)(void *))); +}; + +/*% + * This is the protocols map class. + */ +struct irs_pr { + void * private; + void (*close) __P((struct irs_pr *)); + struct protoent *(*byname) __P((struct irs_pr *, const char *)); + struct protoent *(*bynumber) __P((struct irs_pr *, int)); + struct protoent *(*next) __P((struct irs_pr *)); + void (*rewind) __P((struct irs_pr *)); + void (*minimize) __P((struct irs_pr *)); + struct __res_state * (*res_get) __P((struct irs_pr *)); + void (*res_set) __P((struct irs_pr *, res_state, + void (*)(void *))); +}; + +/*% + * This is the hosts map class. + */ +struct irs_ho { + void * private; + void (*close) __P((struct irs_ho *)); + struct hostent *(*byname) __P((struct irs_ho *, const char *)); + struct hostent *(*byname2) __P((struct irs_ho *, const char *, int)); + struct hostent *(*byaddr) __P((struct irs_ho *, + const void *, int, int)); + struct hostent *(*next) __P((struct irs_ho *)); + void (*rewind) __P((struct irs_ho *)); + void (*minimize) __P((struct irs_ho *)); + struct __res_state * (*res_get) __P((struct irs_ho *)); + void (*res_set) __P((struct irs_ho *, res_state, + void (*)(void *))); + struct addrinfo *(*addrinfo) __P((struct irs_ho *, const char *, + const struct addrinfo *)); +}; + +/*% + * This is the networks map class. + */ +struct irs_nw { + void * private; + void (*close) __P((struct irs_nw *)); + struct nwent * (*byname) __P((struct irs_nw *, const char *, int)); + struct nwent * (*byaddr) __P((struct irs_nw *, void *, int, int)); + struct nwent * (*next) __P((struct irs_nw *)); + void (*rewind) __P((struct irs_nw *)); + void (*minimize) __P((struct irs_nw *)); + struct __res_state * (*res_get) __P((struct irs_nw *)); + void (*res_set) __P((struct irs_nw *, res_state, + void (*)(void *))); +}; + +/*% + * This is the netgroups map class. + */ +struct irs_ng { + void * private; + void (*close) __P((struct irs_ng *)); + int (*next) __P((struct irs_ng *, const char **, + const char **, const char **)); + int (*test) __P((struct irs_ng *, const char *, + const char *, const char *, + const char *)); + void (*rewind) __P((struct irs_ng *, const char *)); + void (*minimize) __P((struct irs_ng *)); +}; + +/*% + * This is the generic map class, which copies the front of all others. + */ +struct irs_map { + void * private; + void (*close) __P((void *)); +}; + +/*% + * This is the accessor class. It contains pointers to all of the + * initializers for the map classes for a particular accessor. + */ +struct irs_acc { + void * private; + void (*close) __P((struct irs_acc *)); + struct irs_gr * (*gr_map) __P((struct irs_acc *)); + struct irs_pw * (*pw_map) __P((struct irs_acc *)); + struct irs_sv * (*sv_map) __P((struct irs_acc *)); + struct irs_pr * (*pr_map) __P((struct irs_acc *)); + struct irs_ho * (*ho_map) __P((struct irs_acc *)); + struct irs_nw * (*nw_map) __P((struct irs_acc *)); + struct irs_ng * (*ng_map) __P((struct irs_acc *)); + struct __res_state * (*res_get) __P((struct irs_acc *)); + void (*res_set) __P((struct irs_acc *, res_state, + void (*)(void *))); +}; + +/*% + * This is because the official definition of "struct netent" has no + * concept of CIDR even though it allows variant address families (on + * output but not input). The compatibility stubs convert the structs + * below into "struct netent"'s. + */ +struct nwent { + char *n_name; /*%< official name of net */ + char **n_aliases; /*%< alias list */ + int n_addrtype; /*%< net address type */ + void *n_addr; /*%< network address */ + int n_length; /*%< address length, in bits */ +}; + +/*% + * Hide external function names from POSIX. + */ +#define irs_gen_acc __irs_gen_acc +#define irs_lcl_acc __irs_lcl_acc +#define irs_dns_acc __irs_dns_acc +#define irs_nis_acc __irs_nis_acc +#define irs_irp_acc __irs_irp_acc +#define irs_destroy __irs_destroy +#define irs_dns_gr __irs_dns_gr +#define irs_dns_ho __irs_dns_ho +#define irs_dns_nw __irs_dns_nw +#define irs_dns_pr __irs_dns_pr +#define irs_dns_pw __irs_dns_pw +#define irs_dns_sv __irs_dns_sv +#define irs_gen_gr __irs_gen_gr +#define irs_gen_ho __irs_gen_ho +#define irs_gen_ng __irs_gen_ng +#define irs_gen_nw __irs_gen_nw +#define irs_gen_pr __irs_gen_pr +#define irs_gen_pw __irs_gen_pw +#define irs_gen_sv __irs_gen_sv +#define irs_irp_get_full_response __irs_irp_get_full_response +#define irs_irp_gr __irs_irp_gr +#define irs_irp_ho __irs_irp_ho +#define irs_irp_is_connected __irs_irp_is_connected +#define irs_irp_ng __irs_irp_ng +#define irs_irp_nw __irs_irp_nw +#define irs_irp_pr __irs_irp_pr +#define irs_irp_pw __irs_irp_pw +#define irs_irp_read_line __irs_irp_read_line +#define irs_irp_sv __irs_irp_sv +#define irs_lcl_gr __irs_lcl_gr +#define irs_lcl_ho __irs_lcl_ho +#define irs_lcl_ng __irs_lcl_ng +#define irs_lcl_nw __irs_lcl_nw +#define irs_lcl_pr __irs_lcl_pr +#define irs_lcl_pw __irs_lcl_pw +#define irs_lcl_sv __irs_lcl_sv +#define irs_nis_gr __irs_nis_gr +#define irs_nis_ho __irs_nis_ho +#define irs_nis_ng __irs_nis_ng +#define irs_nis_nw __irs_nis_nw +#define irs_nis_pr __irs_nis_pr +#define irs_nis_pw __irs_nis_pw +#define irs_nis_sv __irs_nis_sv +#define net_data_create __net_data_create +#define net_data_destroy __net_data_destroy +#define net_data_minimize __net_data_minimize + +/*% + * Externs. + */ +extern struct irs_acc * irs_gen_acc __P((const char *, const char *)); +extern struct irs_acc * irs_lcl_acc __P((const char *)); +extern struct irs_acc * irs_dns_acc __P((const char *)); +extern struct irs_acc * irs_nis_acc __P((const char *)); +extern struct irs_acc * irs_irp_acc __P((const char *)); + +extern void irs_destroy __P((void)); + +/*% + * These forward declarations are for the semi-private functions in + * the get*.c files. Each of these funcs implements the real get* + * functionality and the standard versions are just wrappers that + * call these. Apart from the wrappers, only irpd is expected to + * call these directly, hence these decls are put here and not in + * the /usr/include replacements. + */ + +struct net_data; /*%< forward */ +/* + * net_data_create gets a singleton net_data object. net_data_init + * creates as many net_data objects as times it is called. Clients using + * the default interface will use net_data_create by default. Servers will + * probably want net_data_init (one call per client) + */ +struct net_data *net_data_create __P((const char *)); +struct net_data *net_data_init __P((const char *)); +void net_data_destroy __P((void *)); + +extern struct group *getgrent_p __P((struct net_data *)); +extern struct group *getgrnam_p __P((const char *, struct net_data *)); +extern struct group *getgrgid_p __P((gid_t, struct net_data *)); +extern int setgroupent_p __P((int, struct net_data *)); +extern void endgrent_p __P((struct net_data *)); +extern int getgrouplist_p __P((const char *, gid_t, gid_t *, int *, + struct net_data *)); + +#ifdef SETGRENT_VOID +extern void setgrent_p __P((struct net_data *)); +#else +extern int setgrent_p __P((struct net_data *)); +#endif + +extern struct hostent *gethostbyname_p __P((const char *, + struct net_data *)); +extern struct hostent *gethostbyname2_p __P((const char *, int, + struct net_data *)); +extern struct hostent *gethostbyaddr_p __P((const char *, int, int, + struct net_data *)); +extern struct hostent *gethostent_p __P((struct net_data *)); +extern void sethostent_p __P((int, struct net_data *)); +extern void endhostent_p __P((struct net_data *)); +extern struct hostent *getipnodebyname_p __P((const char *, int, int, int *, + struct net_data *)); +extern struct hostent *getipnodebyaddr_p __P((const void *, size_t, + int, int *, struct net_data *)); + +extern struct netent *getnetent_p __P((struct net_data *)); +extern struct netent *getnetbyname_p __P((const char *, struct net_data *)); +extern struct netent *getnetbyaddr_p __P((unsigned long, int, + struct net_data *)); +extern void setnetent_p __P((int, struct net_data *)); +extern void endnetent_p __P((struct net_data *)); + +extern void setnetgrent_p __P((const char *, struct net_data *)); +extern void endnetgrent_p __P((struct net_data *)); +extern int innetgr_p __P((const char *, const char *, const char *, + const char *, struct net_data *)); +extern int getnetgrent_p __P((const char **, const char **, + const char **, struct net_data *)); + +extern struct protoent *getprotoent_p __P((struct net_data *)); +extern struct protoent *getprotobyname_p __P((const char *, + struct net_data *)); +extern struct protoent *getprotobynumber_p __P((int, struct net_data *)); +extern void setprotoent_p __P((int, struct net_data *)); +extern void endprotoent_p __P((struct net_data *)); + + +extern struct passwd *getpwent_p __P((struct net_data *)); +extern struct passwd *getpwnam_p __P((const char *, struct net_data *)); +extern struct passwd *getpwuid_p __P((uid_t, struct net_data *)); +extern int setpassent_p __P((int, struct net_data *)); +extern void endpwent_p __P((struct net_data *)); + +#ifdef SETPWENT_VOID +extern void setpwent_p __P((struct net_data *)); +#else +extern int setpwent_p __P((struct net_data *)); +#endif + +extern struct servent *getservent_p __P((struct net_data *)); +extern struct servent *getservbyname_p __P((const char *, const char *, + struct net_data *)); +extern struct servent *getservbyport_p __P((int, const char *, + struct net_data *)); +extern void setservent_p __P((int, struct net_data *)); +extern void endservent_p __P((struct net_data *)); + +#endif /*_IRS_H_INCLUDED*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/assertions.h b/usr/src/lib/libresolv2_joy/include/isc/assertions.h new file mode 100644 index 0000000000..68925e73b3 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/assertions.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1997-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: assertions.h,v 1.5 2008/11/14 02:36:51 marka Exp $ + */ + +#ifndef ASSERTIONS_H +#define ASSERTIONS_H 1 + +typedef enum { + assert_require, assert_ensure, assert_insist, assert_invariant +} assertion_type; + +typedef void (*assertion_failure_callback)(const char *, int, assertion_type, + const char *, int); + +/* coverity[+kill] */ +extern assertion_failure_callback __assertion_failed; +void set_assertion_failure_callback(assertion_failure_callback f); +const char *assertion_type_to_text(assertion_type type); + +#if defined(CHECK_ALL) || defined(__COVERITY__) +#define CHECK_REQUIRE 1 +#define CHECK_ENSURE 1 +#define CHECK_INSIST 1 +#define CHECK_INVARIANT 1 +#endif + +#if defined(CHECK_NONE) && !defined(__COVERITY__) +#define CHECK_REQUIRE 0 +#define CHECK_ENSURE 0 +#define CHECK_INSIST 0 +#define CHECK_INVARIANT 0 +#endif + +#ifndef CHECK_REQUIRE +#define CHECK_REQUIRE 1 +#endif + +#ifndef CHECK_ENSURE +#define CHECK_ENSURE 1 +#endif + +#ifndef CHECK_INSIST +#define CHECK_INSIST 1 +#endif + +#ifndef CHECK_INVARIANT +#define CHECK_INVARIANT 1 +#endif + +#if CHECK_REQUIRE != 0 +#define REQUIRE(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_require, \ + #cond, 0), 0))) +#define REQUIRE_ERR(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_require, \ + #cond, 1), 0))) +#else +#define REQUIRE(cond) ((void) (cond)) +#define REQUIRE_ERR(cond) ((void) (cond)) +#endif /* CHECK_REQUIRE */ + +#if CHECK_ENSURE != 0 +#define ENSURE(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_ensure, \ + #cond, 0), 0))) +#define ENSURE_ERR(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_ensure, \ + #cond, 1), 0))) +#else +#define ENSURE(cond) ((void) (cond)) +#define ENSURE_ERR(cond) ((void) (cond)) +#endif /* CHECK_ENSURE */ + +#if CHECK_INSIST != 0 +#define INSIST(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_insist, \ + #cond, 0), 0))) +#define INSIST_ERR(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_insist, \ + #cond, 1), 0))) +#else +#define INSIST(cond) ((void) (cond)) +#define INSIST_ERR(cond) ((void) (cond)) +#endif /* CHECK_INSIST */ + +#if CHECK_INVARIANT != 0 +#define INVARIANT(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_invariant, \ + #cond, 0), 0))) +#define INVARIANT_ERR(cond) \ + ((void) ((cond) || \ + ((__assertion_failed)(__FILE__, __LINE__, assert_invariant, \ + #cond, 1), 0))) +#else +#define INVARIANT(cond) ((void) (cond)) +#define INVARIANT_ERR(cond) ((void) (cond)) +#endif /* CHECK_INVARIANT */ +#endif /* ASSERTIONS_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/ctl.h b/usr/src/lib/libresolv2_joy/include/isc/ctl.h new file mode 100644 index 0000000000..e2ba20201d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/ctl.h @@ -0,0 +1,112 @@ +#ifndef ISC_CTL_H +#define ISC_CTL_H + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1998,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: ctl.h,v 1.5 2005/04/27 04:56:17 sra Exp $ + */ + +/*! \file */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <isc/eventlib.h> + +/* Macros. */ + +#define CTL_MORE 0x0001 /*%< More will be / should be sent. */ +#define CTL_EXIT 0x0002 /*%< Close connection after this. */ +#define CTL_DATA 0x0004 /*%< Go into / this is DATA mode. */ +/* Types. */ + +struct ctl_cctx; +struct ctl_sctx; +struct ctl_sess; +struct ctl_verb; + +enum ctl_severity { ctl_debug, ctl_warning, ctl_error }; + +typedef void (*ctl_logfunc)(enum ctl_severity, const char *, ...); + +typedef void (*ctl_verbfunc)(struct ctl_sctx *, struct ctl_sess *, + const struct ctl_verb *, const char *, + u_int, const void *, void *); + +typedef void (*ctl_srvrdone)(struct ctl_sctx *, struct ctl_sess *, void *); + +typedef void (*ctl_clntdone)(struct ctl_cctx *, void *, const char *, u_int); + +struct ctl_verb { + const char * name; + ctl_verbfunc func; + const char * help; +}; + +/* General symbols. */ + +#define ctl_logger __ctl_logger + +#ifdef __GNUC__ +void ctl_logger(enum ctl_severity, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +#else +void ctl_logger(enum ctl_severity, const char *, ...); +#endif + +/* Client symbols. */ + +#define ctl_client __ctl_client +#define ctl_endclient __ctl_endclient +#define ctl_command __ctl_command + +struct ctl_cctx * ctl_client(evContext, const struct sockaddr *, size_t, + const struct sockaddr *, size_t, + ctl_clntdone, void *, + u_int, ctl_logfunc); +void ctl_endclient(struct ctl_cctx *); +int ctl_command(struct ctl_cctx *, const char *, size_t, + ctl_clntdone, void *); + +/* Server symbols. */ + +#define ctl_server __ctl_server +#define ctl_endserver __ctl_endserver +#define ctl_response __ctl_response +#define ctl_sendhelp __ctl_sendhelp +#define ctl_getcsctx __ctl_getcsctx +#define ctl_setcsctx __ctl_setcsctx + +struct ctl_sctx * ctl_server(evContext, const struct sockaddr *, size_t, + const struct ctl_verb *, + u_int, u_int, + u_int, int, int, + ctl_logfunc, void *); +void ctl_endserver(struct ctl_sctx *); +void ctl_response(struct ctl_sess *, u_int, + const char *, u_int, const void *, + ctl_srvrdone, void *, + const char *, size_t); +void ctl_sendhelp(struct ctl_sess *, u_int); +void * ctl_getcsctx(struct ctl_sess *); +void * ctl_setcsctx(struct ctl_sess *, void *); + +#endif /*ISC_CTL_H*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/dst.h b/usr/src/lib/libresolv2_joy/include/isc/dst.h new file mode 100644 index 0000000000..90a9e67468 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/dst.h @@ -0,0 +1,168 @@ +#ifndef DST_H +#define DST_H + +#ifndef HAS_DST_KEY +typedef struct dst_key { + char *dk_key_name; /*%< name of the key */ + int dk_key_size; /*%< this is the size of the key in bits */ + int dk_proto; /*%< what protocols this key can be used for */ + int dk_alg; /*%< algorithm number from key record */ + u_int32_t dk_flags; /*%< and the flags of the public key */ + u_int16_t dk_id; /*%< identifier of the key */ +} DST_KEY; +#endif /* HAS_DST_KEY */ +/* + * do not taint namespace + */ +#define dst_bsafe_init __dst_bsafe_init +#define dst_buffer_to_key __dst_buffer_to_key +#define dst_check_algorithm __dst_check_algorithm +#define dst_compare_keys __dst_compare_keys +#define dst_cylink_init __dst_cylink_init +#define dst_dnskey_to_key __dst_dnskey_to_key +#define dst_eay_dss_init __dst_eay_dss_init +#define dst_free_key __dst_free_key +#define dst_generate_key __dst_generate_key +#define dst_hmac_md5_init __dst_hmac_md5_init +#define dst_init __dst_init +#define dst_key_to_buffer __dst_key_to_buffer +#define dst_key_to_dnskey __dst_key_to_dnskey +#define dst_read_key __dst_read_key +#define dst_rsaref_init __dst_rsaref_init +#define dst_s_build_filename __dst_s_build_filename +#define dst_s_calculate_bits __dst_s_calculate_bits +#define dst_s_conv_bignum_b64_to_u8 __dst_s_conv_bignum_b64_to_u8 +#define dst_s_conv_bignum_u8_to_b64 __dst_s_conv_bignum_u8_to_b64 +#define dst_s_dns_key_id __dst_s_dns_key_id +#define dst_s_dump __dst_s_dump +#define dst_s_filename_length __dst_s_filename_length +#define dst_s_fopen __dst_s_fopen +#define dst_s_get_int16 __dst_s_get_int16 +#define dst_s_get_int32 __dst_s_get_int32 +#define dst_s_id_calc __dst_s_id_calc +#define dst_s_put_int16 __dst_s_put_int16 +#define dst_s_put_int32 __dst_s_put_int32 +#define dst_s_quick_random __dst_s_quick_random +#define dst_s_quick_random_set __dst_s_quick_random_set +#define dst_s_random __dst_s_random +#define dst_s_semi_random __dst_s_semi_random +#define dst_s_verify_str __dst_s_verify_str +#define dst_sig_size __dst_sig_size +#define dst_sign_data __dst_sign_data +#define dst_verify_data __dst_verify_data +#define dst_write_key __dst_write_key + +/* + * DST Crypto API defintions + */ +void dst_init(void); +int dst_check_algorithm(const int); + + +int dst_sign_data(const int, /*!< specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *, /*!< the key to use */ + void **, /*!< pointer to state structure */ + const u_char *, /*!< data to be signed */ + const int, /*!< length of input data */ + u_char *, /*!< buffer to write signature to */ + const int); /*!< size of output buffer */ +int dst_verify_data(const int, /*!< specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *, /*!< the key to use */ + void **, /*!< pointer to state structure */ + const u_char *, /*!< data to be verified */ + const int, /*!< length of input data */ + const u_char *, /*!< buffer containing signature */ + const int); /*!< length of signature */ +DST_KEY *dst_read_key(const char *, /*!< name of key */ + const u_int16_t, /*!< key tag identifier */ + const int, /*!< key algorithm */ + const int); /*!< Private/PublicKey wanted */ +int dst_write_key(const DST_KEY *, /*!< key to write out */ + const int); /*!< Public/Private */ +DST_KEY *dst_dnskey_to_key(const char *, /*!< KEY record name */ + const u_char *, /*!< KEY RDATA */ + const int); /*!< size of input buffer */ +int dst_key_to_dnskey(const DST_KEY *, /*!< key to translate */ + u_char *, /*!< output buffer */ + const int); /*!< size of out_storage */ +DST_KEY *dst_buffer_to_key(const char *, /*!< name of the key */ + const int, /*!< algorithm */ + const int, /*!< dns flags */ + const int, /*!< dns protocol */ + const u_char *, /*!< key in dns wire fmt */ + const int); /*!< size of key */ +int dst_key_to_buffer(DST_KEY *, u_char *, int); + +DST_KEY *dst_generate_key(const char *, /*!< name of new key */ + const int, /*!< key algorithm to generate */ + const int, /*!< size of new key */ + const int, /*!< alg dependent parameter */ + const int, /*!< key DNS flags */ + const int); /*!< key DNS protocol */ +DST_KEY *dst_free_key(DST_KEY *); +int dst_compare_keys(const DST_KEY *, const DST_KEY *); + +int dst_sig_size(DST_KEY *); + + +/* support for dns key tags/ids */ +u_int16_t dst_s_dns_key_id(const u_char *, const int); +u_int16_t dst_s_id_calc(const u_char *, const int); + +/* Used by callers as well as by the library. */ +#define RAW_KEY_SIZE 8192 /*%< large enough to store any key */ +/* DST_API control flags */ +/* These are used used in functions dst_sign_data and dst_verify_data */ +#define SIG_MODE_INIT 1 /*%< initialize digest */ +#define SIG_MODE_UPDATE 2 /*%< add data to digest */ +#define SIG_MODE_FINAL 4 /*%< generate/verify signature */ +#define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL) + +/* Flags for dst_read_private_key() */ +#define DST_FORCE_READ 0x1000000 +#define DST_CAN_SIGN 0x010F +#define DST_NO_AUTHEN 0x8000 +#define DST_EXTEND_FLAG 0x1000 +#define DST_STANDARD 0 +#define DST_PRIVATE 0x2000000 +#define DST_PUBLIC 0x4000000 +#define DST_RAND_SEMI 1 +#define DST_RAND_STD 2 +#define DST_RAND_KEY 3 +#define DST_RAND_DSS 4 + + +/* DST algorithm codes */ +#define KEY_RSA 1 +#define KEY_DH 2 +#define KEY_DSA 3 +#define KEY_PRIVATE 254 +#define KEY_EXPAND 255 +#define KEY_HMAC_MD5 157 +#define KEY_HMAC_SHA1 158 +#define UNKNOWN_KEYALG 0 +#define DST_MAX_ALGS KEY_HMAC_SHA1 + +/* DST constants to locations in KEY record changes in new KEY record */ +#define DST_FLAGS_SIZE 2 +#define DST_KEY_PROT 2 +#define DST_KEY_ALG 3 +#define DST_EXT_FLAG 4 +#define DST_KEY_START 4 + +#ifndef SIGN_F_NOKEY +#define SIGN_F_NOKEY 0xC000 +#endif + +/* error codes from dst routines */ +#define SIGN_INIT_FAILURE (-23) +#define SIGN_UPDATE_FAILURE (-24) +#define SIGN_FINAL_FAILURE (-25) +#define VERIFY_INIT_FAILURE (-26) +#define VERIFY_UPDATE_FAILURE (-27) +#define VERIFY_FINAL_FAILURE (-28) +#define MISSING_KEY_OR_SIGNATURE (-30) +#define UNSUPPORTED_KEYALG (-31) + +#endif /* DST_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/eventlib.h b/usr/src/lib/libresolv2_joy/include/isc/eventlib.h new file mode 100644 index 0000000000..a4cfdf9092 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/eventlib.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1995-1999, 2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* eventlib.h - exported interfaces for eventlib + * vix 09sep95 [initial] + * + * $Id: eventlib.h,v 1.7 2008/11/14 02:36:51 marka Exp $ + */ + +#ifndef _EVENTLIB_H +#define _EVENTLIB_H + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/time.h> +#include <stdio.h> + +#include <isc/platform.h> + +#ifndef __P +# define __EVENTLIB_P_DEFINED +# ifdef __STDC__ +# define __P(x) x +# else +# define __P(x) () +# endif +#endif + +/* In the absence of branded types... */ +typedef struct { void *opaque; } evConnID; +typedef struct { void *opaque; } evFileID; +typedef struct { void *opaque; } evStreamID; +typedef struct { void *opaque; } evTimerID; +typedef struct { void *opaque; } evWaitID; +typedef struct { void *opaque; } evContext; +typedef struct { void *opaque; } evEvent; + +#define evInitID(id) ((id)->opaque = NULL) +#define evTestID(id) ((id).opaque != NULL) + +typedef void (*evConnFunc)__P((evContext, void *, int, const void *, int, + const void *, int)); +typedef void (*evFileFunc)__P((evContext, void *, int, int)); +typedef void (*evStreamFunc)__P((evContext, void *, int, int)); +typedef void (*evTimerFunc)__P((evContext, void *, + struct timespec, struct timespec)); +typedef void (*evWaitFunc)__P((evContext, void *, const void *)); + +typedef struct { unsigned char mask[256/8]; } evByteMask; +#define EV_BYTEMASK_BYTE(b) ((b) / 8) +#define EV_BYTEMASK_MASK(b) (1 << ((b) % 8)) +#define EV_BYTEMASK_SET(bm, b) \ + ((bm).mask[EV_BYTEMASK_BYTE(b)] |= EV_BYTEMASK_MASK(b)) +#define EV_BYTEMASK_CLR(bm, b) \ + ((bm).mask[EV_BYTEMASK_BYTE(b)] &= ~EV_BYTEMASK_MASK(b)) +#define EV_BYTEMASK_TST(bm, b) \ + ((bm).mask[EV_BYTEMASK_BYTE(b)] & EV_BYTEMASK_MASK(b)) + +#define EV_POLL 1 +#define EV_WAIT 2 +#define EV_NULL 4 + +#define EV_READ 1 +#define EV_WRITE 2 +#define EV_EXCEPT 4 + +#define EV_WASNONBLOCKING 8 /* Internal library use. */ + +/* eventlib.c */ +#define evCreate __evCreate +#define evSetDebug __evSetDebug +#define evDestroy __evDestroy +#define evGetNext __evGetNext +#define evDispatch __evDispatch +#define evDrop __evDrop +#define evMainLoop __evMainLoop +#define evHighestFD __evHighestFD +#define evGetOption __evGetOption +#define evSetOption __evSetOption + +int evCreate __P((evContext *)); +void evSetDebug __P((evContext, int, FILE *)); +int evDestroy __P((evContext)); +int evGetNext __P((evContext, evEvent *, int)); +int evDispatch __P((evContext, evEvent)); +void evDrop __P((evContext, evEvent)); +int evMainLoop __P((evContext)); +int evHighestFD __P((evContext)); +int evGetOption __P((evContext *, const char *, int *)); +int evSetOption __P((evContext *, const char *, int)); + +/* ev_connects.c */ +#define evListen __evListen +#define evConnect __evConnect +#define evCancelConn __evCancelConn +#define evHold __evHold +#define evUnhold __evUnhold +#define evTryAccept __evTryAccept + +int evListen __P((evContext, int, int, evConnFunc, void *, evConnID *)); +int evConnect __P((evContext, int, const void *, int, + evConnFunc, void *, evConnID *)); +int evCancelConn __P((evContext, evConnID)); +int evHold __P((evContext, evConnID)); +int evUnhold __P((evContext, evConnID)); +int evTryAccept __P((evContext, evConnID, int *)); + +/* ev_files.c */ +#define evSelectFD __evSelectFD +#define evDeselectFD __evDeselectFD + +int evSelectFD __P((evContext, int, int, evFileFunc, void *, evFileID *)); +int evDeselectFD __P((evContext, evFileID)); + +/* ev_streams.c */ +#define evConsIovec __evConsIovec +#define evWrite __evWrite +#define evRead __evRead +#define evTimeRW __evTimeRW +#define evUntimeRW __evUntimeRW +#define evCancelRW __evCancelRW + +struct iovec evConsIovec __P((void *, size_t)); +int evWrite __P((evContext, int, const struct iovec *, int, + evStreamFunc func, void *, evStreamID *)); +int evRead __P((evContext, int, const struct iovec *, int, + evStreamFunc func, void *, evStreamID *)); +int evTimeRW __P((evContext, evStreamID, evTimerID timer)); +int evUntimeRW __P((evContext, evStreamID)); +int evCancelRW __P((evContext, evStreamID)); + +/* ev_timers.c */ +#define evConsTime __evConsTime +#define evAddTime __evAddTime +#define evSubTime __evSubTime +#define evCmpTime __evCmpTime +#define evTimeSpec __evTimeSpec +#define evTimeVal __evTimeVal + +#define evNowTime __evNowTime +#define evUTCTime __evUTCTime +#define evLastEventTime __evLastEventTime +#define evSetTimer __evSetTimer +#define evClearTimer __evClearTimer +#define evConfigTimer __evConfigTimer +#define evResetTimer __evResetTimer +#define evSetIdleTimer __evSetIdleTimer +#define evClearIdleTimer __evClearIdleTimer +#define evResetIdleTimer __evResetIdleTimer +#define evTouchIdleTimer __evTouchIdleTimer + +struct timespec evConsTime __P((time_t sec, long nsec)); +struct timespec evAddTime __P((struct timespec, struct timespec)); +struct timespec evSubTime __P((struct timespec, struct timespec)); +struct timespec evNowTime __P((void)); +struct timespec evUTCTime __P((void)); +struct timespec evLastEventTime __P((evContext)); +struct timespec evTimeSpec __P((struct timeval)); +struct timeval evTimeVal __P((struct timespec)); +int evCmpTime __P((struct timespec, struct timespec)); +int evSetTimer __P((evContext, evTimerFunc, void *, struct timespec, + struct timespec, evTimerID *)); +int evClearTimer __P((evContext, evTimerID)); +int evConfigTimer __P((evContext, evTimerID, const char *param, + int value)); +int evResetTimer __P((evContext, evTimerID, evTimerFunc, void *, + struct timespec, struct timespec)); +int evSetIdleTimer __P((evContext, evTimerFunc, void *, struct timespec, + evTimerID *)); +int evClearIdleTimer __P((evContext, evTimerID)); +int evResetIdleTimer __P((evContext, evTimerID, evTimerFunc, void *, + struct timespec)); +int evTouchIdleTimer __P((evContext, evTimerID)); + +/* ev_waits.c */ +#define evWaitFor __evWaitFor +#define evDo __evDo +#define evUnwait __evUnwait +#define evDefer __evDefer + +int evWaitFor __P((evContext, const void *, evWaitFunc, void *, evWaitID *)); +int evDo __P((evContext, const void *)); +int evUnwait __P((evContext, evWaitID)); +int evDefer __P((evContext, evWaitFunc, void *)); + +#ifdef __EVENTLIB_P_DEFINED +# undef __P +#endif + +#endif /*_EVENTLIB_H*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/heap.h b/usr/src/lib/libresolv2_joy/include/isc/heap.h new file mode 100644 index 0000000000..384d507cf5 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/heap.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +typedef int (*heap_higher_priority_func)(void *, void *); +typedef void (*heap_index_func)(void *, int); +typedef void (*heap_for_each_func)(void *, void *); + +typedef struct heap_context { + int array_size; + int array_size_increment; + int heap_size; + void **heap; + heap_higher_priority_func higher_priority; + heap_index_func index; +} *heap_context; + +#define heap_new __heap_new +#define heap_free __heap_free +#define heap_insert __heap_insert +#define heap_delete __heap_delete +#define heap_increased __heap_increased +#define heap_decreased __heap_decreased +#define heap_element __heap_element +#define heap_for_each __heap_for_each + +heap_context heap_new(heap_higher_priority_func, heap_index_func, int); +int heap_free(heap_context); +int heap_insert(heap_context, void *); +int heap_delete(heap_context, int); +int heap_increased(heap_context, int); +int heap_decreased(heap_context, int); +void * heap_element(heap_context, int); +int heap_for_each(heap_context, heap_for_each_func, void *); + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h b/usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h new file mode 100644 index 0000000000..244b3e3460 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: irpmarshall.h,v 1.4 2005/04/27 04:56:17 sra Exp $ + */ + +#ifndef _IRPMARSHALL_H_INCLUDED +#define _IRPMARSHALL_H_INCLUDED + +/* Hide function names */ +#define irp_marshall_gr __irp_marshall_gr +#define irp_marshall_ho __irp_marshall_ho +#define irp_marshall_ne __irp_marshall_ne +#define irp_marshall_ng __irp_marshall_ng +#define irp_marshall_nw __irp_marshall_nw +#define irp_marshall_pr __irp_marshall_pr +#define irp_marshall_pw __irp_marshall_pw +#define irp_marshall_sv __irp_marshall_sv +#define irp_unmarshall_gr __irp_unmarshall_gr +#define irp_unmarshall_ho __irp_unmarshall_ho +#define irp_unmarshall_ne __irp_unmarshall_ne +#define irp_unmarshall_ng __irp_unmarshall_ng +#define irp_unmarshall_nw __irp_unmarshall_nw +#define irp_unmarshall_pr __irp_unmarshall_pr +#define irp_unmarshall_pw __irp_unmarshall_pw +#define irp_unmarshall_sv __irp_unmarshall_sv + +#define MAXPADDRSIZE (sizeof "255.255.255.255" + 1) +#define ADDR_T_STR(x) (x == AF_INET ? "AF_INET" :\ + (x == AF_INET6 ? "AF_INET6" : "UNKNOWN")) + +/* See comment below on usage */ +int irp_marshall_pw(const struct passwd *, char **, size_t *); +int irp_unmarshall_pw(struct passwd *, char *); +int irp_marshall_gr(const struct group *, char **, size_t *); +int irp_unmarshall_gr(struct group *, char *); +int irp_marshall_sv(const struct servent *, char **, size_t *); +int irp_unmarshall_sv(struct servent *, char *); +int irp_marshall_pr(struct protoent *, char **, size_t *); +int irp_unmarshall_pr(struct protoent *, char *); +int irp_marshall_ho(struct hostent *, char **, size_t *); +int irp_unmarshall_ho(struct hostent *, char *); +int irp_marshall_ng(const char *, const char *, const char *, + char **, size_t *); +int irp_unmarshall_ng(const char **, const char **, const char **, char *); +int irp_marshall_nw(struct nwent *, char **, size_t *); +int irp_unmarshall_nw(struct nwent *, char *); +int irp_marshall_ne(struct netent *, char **, size_t *); +int irp_unmarshall_ne(struct netent *, char *); + +/*! \file + * \brief + * Functions to marshall and unmarshall various system data structures. We + * use a printable ascii format that is as close to various system config + * files as reasonable (e.g. /etc/passwd format). + * + * We are not forgiving with unmarhsalling misformatted buffers. In + * particular whitespace in fields is not ignored. So a formatted password + * entry "brister :1364:100:...." will yield a username of "brister " + * + * We potentially do a lot of mallocs to fill fields that are of type + * (char **) like a hostent h_addr field. Building (for example) the + * h_addr field and its associated addresses all in one buffer is + * certainly possible, but not done here. + * + * The following description is true for all the marshalling functions: + * + * int irp_marshall_XX(struct yyyy *XX, char **buffer, size_t *len); + * + * The argument XX (of type struct passwd for example) is marshalled in the + * buffer pointed at by *BUFFER, which is of length *LEN. Returns 0 + * on success and -1 on failure. Failure will occur if *LEN is + * smaller than needed. + * + * If BUFFER is NULL, then *LEN is set to the size of the buffer + * needed to marshall the data and no marshalling is actually done. + * + * If *BUFFER is NULL, then a buffer large enough will be allocated + * with memget() and the size allocated will be stored in *LEN. An extra 2 + * bytes will be allocated for the client to append CRLF if wanted. The + * value of *LEN will include these two bytes. + * + * All the marshalling functions produce a buffer with the fields + * separated by colons (except for the hostent marshalling, which uses '@' + * to separate fields). Fields that have multiple subfields (like the + * gr_mem field in struct group) have their subparts separated by + * commas. + * + * int irp_unmarshall_XX(struct YYYYY *XX, char *buffer); + * + * The unmashalling functions break apart the buffer and store the + * values in the struct pointed to by XX. All pointer values inside + * XX are allocated with malloc. All arrays of pointers have a NULL + * as the last element. + */ + +#endif diff --git a/usr/src/lib/libresolv2_joy/include/isc/list.h b/usr/src/lib/libresolv2_joy/include/isc/list.h new file mode 100644 index 0000000000..5fe9031141 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/list.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef LIST_H +#define LIST_H 1 +#include <isc/assertions.h> + +#define LIST(type) struct { type *head, *tail; } +#define INIT_LIST(list) \ + do { (list).head = NULL; (list).tail = NULL; } while (0) + +#define LINK(type) struct { type *prev, *next; } +#define INIT_LINK_TYPE(elt, link, type) \ + do { \ + (elt)->link.prev = (type *)(-1); \ + (elt)->link.next = (type *)(-1); \ + } while (0) +#define INIT_LINK(elt, link) \ + INIT_LINK_TYPE(elt, link, void) +#define LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1) && \ + (void *)((elt)->link.next) != (void *)(-1)) + +#define HEAD(list) ((list).head) +#define TAIL(list) ((list).tail) +#define EMPTY(list) ((list).head == NULL) + +#define PREPEND(list, elt, link) \ + do { \ + INSIST(!LINKED(elt, link));\ + if ((list).head != NULL) \ + (list).head->link.prev = (elt); \ + else \ + (list).tail = (elt); \ + (elt)->link.prev = NULL; \ + (elt)->link.next = (list).head; \ + (list).head = (elt); \ + } while (0) + +#define APPEND(list, elt, link) \ + do { \ + INSIST(!LINKED(elt, link));\ + if ((list).tail != NULL) \ + (list).tail->link.next = (elt); \ + else \ + (list).head = (elt); \ + (elt)->link.prev = (list).tail; \ + (elt)->link.next = NULL; \ + (list).tail = (elt); \ + } while (0) + +#define UNLINK_TYPE(list, elt, link, type) \ + do { \ + INSIST(LINKED(elt, link));\ + if ((elt)->link.next != NULL) \ + (elt)->link.next->link.prev = (elt)->link.prev; \ + else { \ + INSIST((list).tail == (elt)); \ + (list).tail = (elt)->link.prev; \ + } \ + if ((elt)->link.prev != NULL) \ + (elt)->link.prev->link.next = (elt)->link.next; \ + else { \ + INSIST((list).head == (elt)); \ + (list).head = (elt)->link.next; \ + } \ + INIT_LINK_TYPE(elt, link, type); \ + } while (0) +#define UNLINK(list, elt, link) \ + UNLINK_TYPE(list, elt, link, void) + +#define PREV(elt, link) ((elt)->link.prev) +#define NEXT(elt, link) ((elt)->link.next) + +#define INSERT_BEFORE(list, before, elt, link) \ + do { \ + INSIST(!LINKED(elt, link));\ + if ((before)->link.prev == NULL) \ + PREPEND(list, elt, link); \ + else { \ + (elt)->link.prev = (before)->link.prev; \ + (before)->link.prev = (elt); \ + (elt)->link.prev->link.next = (elt); \ + (elt)->link.next = (before); \ + } \ + } while (0) + +#define INSERT_AFTER(list, after, elt, link) \ + do { \ + INSIST(!LINKED(elt, link));\ + if ((after)->link.next == NULL) \ + APPEND(list, elt, link); \ + else { \ + (elt)->link.next = (after)->link.next; \ + (after)->link.next = (elt); \ + (elt)->link.next->link.prev = (elt); \ + (elt)->link.prev = (after); \ + } \ + } while (0) + +#define ENQUEUE(list, elt, link) APPEND(list, elt, link) +#define DEQUEUE(list, elt, link) UNLINK(list, elt, link) + +#endif /* LIST_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/logging.h b/usr/src/lib/libresolv2_joy/include/isc/logging.h new file mode 100644 index 0000000000..c539443ff8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/logging.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef LOGGING_H +#define LOGGING_H + +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> + +#define log_critical (-5) +#define log_error (-4) +#define log_warning (-3) +#define log_notice (-2) +#define log_info (-1) +#define log_debug(level) (level) + +typedef enum { log_syslog, log_file, log_null } log_channel_type; + +#define LOG_MAX_VERSIONS 99 + +#define LOG_CLOSE_STREAM 0x0001 +#define LOG_TIMESTAMP 0x0002 +#define LOG_TRUNCATE 0x0004 +#define LOG_USE_CONTEXT_LEVEL 0x0008 +#define LOG_PRINT_LEVEL 0x0010 +#define LOG_REQUIRE_DEBUG 0x0020 +#define LOG_CHANNEL_BROKEN 0x0040 +#define LOG_PRINT_CATEGORY 0x0080 +#define LOG_CHANNEL_OFF 0x0100 + +typedef struct log_context *log_context; +typedef struct log_channel *log_channel; + +#define LOG_OPTION_DEBUG 0x01 +#define LOG_OPTION_LEVEL 0x02 + +#define log_open_stream __log_open_stream +#define log_close_stream __log_close_stream +#define log_get_stream __log_get_stream +#define log_get_filename __log_get_filename +#define log_check_channel __log_check_channel +#define log_check __log_check +#define log_vwrite __log_vwrite +#define log_write __log_write +#define log_new_context __log_new_context +#define log_free_context __log_free_context +#define log_add_channel __log_add_channel +#define log_remove_channel __log_remove_channel +#define log_option __log_option +#define log_category_is_active __log_category_is_active +#define log_new_syslog_channel __log_new_syslog_channel +#define log_new_file_channel __log_new_file_channel +#define log_set_file_owner __log_set_file_owner +#define log_new_null_channel __log_new_null_channel +#define log_inc_references __log_inc_references +#define log_dec_references __log_dec_references +#define log_get_channel_type __log_get_channel_type +#define log_free_channel __log_free_channel +#define log_close_debug_channels __log_close_debug_channels + +FILE * log_open_stream(log_channel); +int log_close_stream(log_channel); +FILE * log_get_stream(log_channel); +char * log_get_filename(log_channel); +int log_check_channel(log_context, int, log_channel); +int log_check(log_context, int, int); +#ifdef __GNUC__ +void log_vwrite(log_context, int, int, const char *, + va_list args) + __attribute__((__format__(__printf__, 4, 0))); +void log_write(log_context, int, int, const char *, ...) + __attribute__((__format__(__printf__, 4, 5))); +#else +void log_vwrite(log_context, int, int, const char *, + va_list args); +void log_write(log_context, int, int, const char *, ...); +#endif +int log_new_context(int, char **, log_context *); +void log_free_context(log_context); +int log_add_channel(log_context, int, log_channel); +int log_remove_channel(log_context, int, log_channel); +int log_option(log_context, int, int); +int log_category_is_active(log_context, int); +log_channel log_new_syslog_channel(unsigned int, int, int); +log_channel log_new_file_channel(unsigned int, int, const char *, + FILE *, unsigned int, + unsigned long); +int log_set_file_owner(log_channel, uid_t, gid_t); +log_channel log_new_null_channel(void); +int log_inc_references(log_channel); +int log_dec_references(log_channel); +log_channel_type log_get_channel_type(log_channel); +int log_free_channel(log_channel); +void log_close_debug_channels(log_context); + +#endif /* !LOGGING_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/memcluster.h b/usr/src/lib/libresolv2_joy/include/isc/memcluster.h new file mode 100644 index 0000000000..0923deb5e7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/memcluster.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,1999 by Internet Software Consortium. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef MEMCLUSTER_H +#define MEMCLUSTER_H + +#include <stdio.h> + +#define meminit __meminit +#ifdef MEMCLUSTER_DEBUG +#define memget(s) __memget_debug(s, __FILE__, __LINE__) +#define memput(p, s) __memput_debug(p, s, __FILE__, __LINE__) +#else /*MEMCLUSTER_DEBUG*/ +#ifdef MEMCLUSTER_RECORD +#define memget(s) __memget_record(s, __FILE__, __LINE__) +#define memput(p, s) __memput_record(p, s, __FILE__, __LINE__) +#else /*MEMCLUSTER_RECORD*/ +#define memget __memget +#define memput __memput +#endif /*MEMCLUSTER_RECORD*/ +#endif /*MEMCLUSTER_DEBUG*/ +#define memstats __memstats +#define memactive __memactive + +int meminit(size_t, size_t); +void * __memget(size_t); +void __memput(void *, size_t); +void * __memget_debug(size_t, const char *, int); +void __memput_debug(void *, size_t, const char *, int); +void * __memget_record(size_t, const char *, int); +void __memput_record(void *, size_t, const char *, int); +void memstats(FILE *); +int memactive(void); + +#endif /* MEMCLUSTER_H */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/misc.h b/usr/src/lib/libresolv2_joy/include/isc/misc.h new file mode 100644 index 0000000000..b54f4ee6ed --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/misc.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1995-2001, 2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: misc.h,v 1.7 2008/11/14 02:36:51 marka Exp $ + */ + +#ifndef _ISC_MISC_H +#define _ISC_MISC_H + +/*! \file */ + +#include <stdio.h> +#include <sys/types.h> + +#define bitncmp __bitncmp +/*#define isc_movefile __isc_movefile */ + +extern int bitncmp(const void *, const void *, int); +extern int isc_movefile(const char *, const char *); + +extern int isc_gethexstring(unsigned char *, size_t, int, FILE *, + int *); +extern void isc_puthexstring(FILE *, const unsigned char *, size_t, + size_t, size_t, const char *); +extern void isc_tohex(const unsigned char *, size_t, char *); + +#endif /*_ISC_MISC_H*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/isc/platform.h b/usr/src/lib/libresolv2_joy/include/isc/platform.h new file mode 100644 index 0000000000..2fc59b61a8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/platform.h @@ -0,0 +1,42 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: platform.h.in,v 1.3 2008/01/23 02:15:56 tbox Exp $ */ + +/*! \file */ + +#ifndef ISC_PLATFORM_H +#define ISC_PLATFORM_H + +/* + * Define if the OS does not define struct timespec. + */ +#undef ISC_PLATFORM_NEEDTIMESPEC +#ifdef ISC_PLATFORM_NEEDTIMESPEC +#include <time.h> /* For time_t */ +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif + +#endif diff --git a/usr/src/lib/libresolv2_joy/include/isc/tree.h b/usr/src/lib/libresolv2_joy/include/isc/tree.h new file mode 100644 index 0000000000..96feaca68d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/isc/tree.h @@ -0,0 +1,59 @@ +/* tree.h - declare structures used by tree library + * + * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes] + * vix 27jun86 [broken out of tree.c] + * + * $Id: tree.h,v 1.3 2005/04/27 04:56:18 sra Exp $ + */ + + +#ifndef _TREE_H_INCLUDED +#define _TREE_H_INCLUDED + + +#ifndef __P +# if defined(__STDC__) || defined(__GNUC__) +# define __P(x) x +# else +# define __P(x) () +# endif +#endif + +/*% + * tree_t is our package-specific anonymous pointer. + */ +#if defined(__STDC__) || defined(__GNUC__) +typedef void *tree_t; +#else +typedef char *tree_t; +#endif + +/*% + * Do not taint namespace + */ +#define tree_add __tree_add +#define tree_delete __tree_delete +#define tree_init __tree_init +#define tree_mung __tree_mung +#define tree_srch __tree_srch +#define tree_trav __tree_trav + + +typedef struct tree_s { + tree_t data; + struct tree_s *left, *right; + short bal; + } + tree; + + +void tree_init __P((tree **)); +tree_t tree_srch __P((tree **, int (*)(), tree_t)); +tree_t tree_add __P((tree **, int (*)(), tree_t, void (*)())); +int tree_delete __P((tree **, int (*)(), tree_t, void (*)())); +int tree_trav __P((tree **, int (*)())); +void tree_mung __P((tree **, void (*)())); + + +#endif /* _TREE_H_INCLUDED */ +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/make_os_version b/usr/src/lib/libresolv2_joy/include/make_os_version new file mode 100755 index 0000000000..3654490fee --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/make_os_version @@ -0,0 +1,34 @@ +#!/bin/sh + +# Copyright (c) 1999 by Sun Microsystems, Inc. +# All rights reserved. +# +#pragma ident "%Z%%M% %I% %E% SMI" + +UNAME_R=`/usr/bin/uname -r` + +OS_MAJOR=`echo $UNAME_R | /usr/bin/sed -e 's/^\([^.]*\).*/\1/'` +OS_MINOR=`echo $UNAME_R | /usr/bin/sed -e 's/^[^.]*\.\([^.]*\).*/\1/'` +OS_VERSION=`echo $UNAME_R | tr '.' '_'` + +cat <<EOF > new_os_version.h +#ifndef OS_VERSION_H +#define OS_VERSION_H + +#define SUNOS_$OS_VERSION +#define OS_MAJOR $OS_MAJOR +#define OS_MINOR $OS_MINOR + +#endif +EOF + +if [ -f os_version.h ]; then + if /usr/bin/cmp -s new_os_version.h os_version.h; then + /usr/bin/rm -f new_os_version.h + else + /usr/bin/rm -f os_version.h + /usr/bin/mv new_os_version.h os_version.h + fi +else + /usr/bin/mv new_os_version.h os_version.h +fi diff --git a/usr/src/lib/libresolv2_joy/include/make_os_version.sh b/usr/src/lib/libresolv2_joy/include/make_os_version.sh new file mode 100644 index 0000000000..3654490fee --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/make_os_version.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# Copyright (c) 1999 by Sun Microsystems, Inc. +# All rights reserved. +# +#pragma ident "%Z%%M% %I% %E% SMI" + +UNAME_R=`/usr/bin/uname -r` + +OS_MAJOR=`echo $UNAME_R | /usr/bin/sed -e 's/^\([^.]*\).*/\1/'` +OS_MINOR=`echo $UNAME_R | /usr/bin/sed -e 's/^[^.]*\.\([^.]*\).*/\1/'` +OS_VERSION=`echo $UNAME_R | tr '.' '_'` + +cat <<EOF > new_os_version.h +#ifndef OS_VERSION_H +#define OS_VERSION_H + +#define SUNOS_$OS_VERSION +#define OS_MAJOR $OS_MAJOR +#define OS_MINOR $OS_MINOR + +#endif +EOF + +if [ -f os_version.h ]; then + if /usr/bin/cmp -s new_os_version.h os_version.h; then + /usr/bin/rm -f new_os_version.h + else + /usr/bin/rm -f os_version.h + /usr/bin/mv new_os_version.h os_version.h + fi +else + /usr/bin/mv new_os_version.h os_version.h +fi diff --git a/usr/src/lib/libresolv2_joy/include/port_after.h b/usr/src/lib/libresolv2_joy/include/port_after.h new file mode 100644 index 0000000000..c3abf4b334 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/port_after.h @@ -0,0 +1,539 @@ +/* + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* + * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2001-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: port_after.h.in,v 1.60 2008/02/28 05:34:17 marka Exp $ */ + +#ifndef port_after_h +#define port_after_h + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/time.h> +#if (!defined(BSD)) || (BSD < 199306) +#include <sys/bitypes.h> +#endif +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif /* HAVE_SYS_SELECT_H */ + +#ifdef REENABLE_SEND +#undef send +#endif + +#undef NEED_PSELECT +#undef HAVE_SA_LEN +#undef HAVE_MINIMUM_IFREQ +#define NEED_DAEMON 1 +#undef NEED_STRSEP +#undef NEED_STRERROR +#ifdef NEED_STRERROR +const char *isc_strerror(int); +#define strerror isc_strerror +#endif +/* HAS_INET6_STRUCTS and HAVE_SIN6_SCOPE_ID are defined by port_ipv6.h + * #define HAS_INET6_STRUCTS 1 + * #define HAVE_SIN6_SCOPE_ID 1 + */ +#include <port_ipv6.h> + +#undef NEED_IN6ADDR_ANY +#undef HAS_IN_ADDR6 +#define HAVE_SOCKADDR_STORAGE 1 +#undef NEED_GETTIMEOFDAY +#define HAVE_STRNDUP +#undef USE_FIONBIO_IOCTL +#undef INNETGR_ARGS + +#undef USE_IFNAMELINKID +#define PORT_NONBLOCK O_NONBLOCK + +#ifndef _POSIX_PATH_MAX +#define _POSIX_PATH_MAX 255 +#endif +#ifndef PATH_MAX +#define PATH_MAX _POSIX_PATH_MAX +#endif + +/* + * We need to know the IPv6 address family number even on IPv4-only systems. + * Note that this is NOT a protocol constant, and that if the system has its + * own AF_INET6, different from ours below, all of BIND's libraries and + * executables will need to be recompiled after the system <sys/socket.h> + * has had this type added. The type number below is correct on most BSD- + * derived systems for which AF_INET6 is defined. + */ +#ifndef AF_INET6 +#define AF_INET6 24 +#endif + +#ifndef PF_INET6 +#define PF_INET6 AF_INET6 +#endif + +#ifdef HAS_IN_ADDR6 +/* Map to pre-RFC structure. */ +#define in6_addr in_addr6 +#endif + +#ifndef HAS_INET6_STRUCTS +/* Replace with structure from later rev of O/S if known. */ +struct in6_addr { + u_int8_t s6_addr[16]; +}; + +#define IN6ADDR_ANY_INIT \ + {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }} + +#define IN6ADDR_LOOPBACK_INIT \ + {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }} + +/* Replace with structure from later rev of O/S if known. */ +struct sockaddr_in6 { +#ifdef HAVE_SA_LEN + u_int8_t sin6_len; /* length of this struct */ + u_int8_t sin6_family; /* AF_INET6 */ +#else + u_int16_t sin6_family; /* AF_INET6 */ +#endif + u_int16_t sin6_port; /* transport layer port # */ + u_int32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + u_int32_t sin6_scope_id; /* set of interfaces for a scope */ +}; +#endif /* HAS_INET6_STRUCTS */ + +#ifdef BROKEN_IN6ADDR_INIT_MACROS +#undef IN6ADDR_ANY_INIT +#undef IN6ADDR_LOOPBACK_INIT +#endif + +#ifdef _AIX +#ifndef IN6ADDR_ANY_INIT +#define IN6ADDR_ANY_INIT {{{ 0, 0, 0, 0 }}} +#endif +#ifndef IN6ADDR_LOOPBACK_INIT +#if BYTE_ORDER == BIG_ENDIAN +#define IN6ADDR_LOOPBACK_INIT {{{ 0, 0, 0, 1 }}} +#else +#define IN6ADDR_LOOPBACK_INIT {{{0, 0, 0, 0x01000000}}} +#endif +#endif +#endif + +#ifndef IN6ADDR_ANY_INIT +#ifdef s6_addr +#define IN6ADDR_ANY_INIT \ + {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}} +#else +#define IN6ADDR_ANY_INIT \ + {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }} +#endif + +#endif +#ifndef IN6ADDR_LOOPBACK_INIT +#ifdef s6_addr +#define IN6ADDR_LOOPBACK_INIT \ + {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}} +#else +#define IN6ADDR_LOOPBACK_INIT \ + {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }} +#endif +#endif + +#ifndef HAVE_SOCKADDR_STORAGE +#define __SS_MAXSIZE 128 +#define __SS_ALLIGSIZE (sizeof (long)) + +struct sockaddr_storage { +#ifdef HAVE_SA_LEN + u_int8_t ss_len; /* address length */ + u_int8_t ss_family; /* address family */ + char __ss_pad1[__SS_ALLIGSIZE - 2 * sizeof(u_int8_t)]; + long __ss_align; + char __ss_pad2[__SS_MAXSIZE - 2 * __SS_ALLIGSIZE]; +#else + u_int16_t ss_family; /* address family */ + char __ss_pad1[__SS_ALLIGSIZE - sizeof(u_int16_t)]; + long __ss_align; + char __ss_pad2[__SS_MAXSIZE - 2 * __SS_ALLIGSIZE]; +#endif +}; +#endif + + +#if !defined(HAS_INET6_STRUCTS) || defined(NEED_IN6ADDR_ANY) +#define in6addr_any isc_in6addr_any +extern const struct in6_addr in6addr_any; +#endif + +/* + * IN6_ARE_ADDR_EQUAL, IN6_IS_ADDR_UNSPECIFIED, IN6_IS_ADDR_V4COMPAT and + * IN6_IS_ADDR_V4MAPPED are broken in glibc 2.1. + */ +#ifdef __GLIBC__ +#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) +#undef IN6_ARE_ADDR_EQUAL +#undef IN6_IS_ADDR_UNSPECIFIED +#undef IN6_IS_ADDR_V4COMPAT +#undef IN6_IS_ADDR_V4MAPPED +#endif +#endif + +#ifndef IN6_ARE_ADDR_EQUAL +#define IN6_ARE_ADDR_EQUAL(a,b) \ + (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0) +#endif + +#ifndef IN6_IS_ADDR_UNSPECIFIED +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + IN6_ARE_ADDR_EQUAL(a, &in6addr_any) +#endif + +#ifndef IN6_IS_ADDR_LOOPBACK +extern const struct in6_addr isc_in6addr_loopback; +#define IN6_IS_ADDR_LOOPBACK(a) \ + IN6_ARE_ADDR_EQUAL(a, &isc_in6addr_loopback) +#endif + +#ifndef IN6_IS_ADDR_V4MAPPED +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((a)->s6_addr[0] == 0x00 && (a)->s6_addr[1] == 0x00 && \ + (a)->s6_addr[2] == 0x00 && (a)->s6_addr[3] == 0x00 && \ + (a)->s6_addr[4] == 0x00 && (a)->s6_addr[5] == 0x00 && \ + (a)->s6_addr[6] == 0x00 && (a)->s6_addr[9] == 0x00 && \ + (a)->s6_addr[8] == 0x00 && (a)->s6_addr[9] == 0x00 && \ + (a)->s6_addr[10] == 0xff && (a)->s6_addr[11] == 0xff) +#endif + +#ifndef IN6_IS_ADDR_SITELOCAL +#define IN6_IS_ADDR_SITELOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) +#endif + +#ifndef IN6_IS_ADDR_LINKLOCAL +#define IN6_IS_ADDR_LINKLOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) +#endif + +#ifndef IN6_IS_ADDR_MULTICAST +#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff) +#endif + +#ifndef __IPV6_ADDR_MC_SCOPE +#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f) +#endif + +#ifndef __IPV6_ADDR_SCOPE_SITELOCAL +#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05 +#endif +#ifndef __IPV6_ADDR_SCOPE_ORGLOCAL +#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08 +#endif + +#ifndef IN6_IS_ADDR_MC_SITELOCAL +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && \ + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL)) +#endif + +#ifndef IN6_IS_ADDR_MC_ORGLOCAL +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && \ + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL)) +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +#ifndef INET6_ADDRSTRLEN +/* sizeof("aaaa:bbbb:cccc:dddd:eeee:ffff:123.123.123.123") */ +#define INET6_ADDRSTRLEN 46 +#endif + +#ifndef MIN +#define MIN(x,y) (((x) <= (y)) ? (x) : (y)) +#endif + +#ifndef MAX +#define MAX(x,y) (((x) >= (y)) ? (x) : (y)) +#endif + +#ifdef NEED_DAEMON +int daemon(int nochdir, int noclose); +#endif + +#ifdef NEED_STRSEP +char * strsep(char **stringp, const char *delim); +#endif + +#ifndef ALIGN +#define ALIGN(p) (((uintptr_t)(p) + (sizeof(long) - 1)) & ~(sizeof(long) - 1)) +#endif + +#ifdef NEED_SETGROUPENT +int setgroupent(int stayopen); +#endif + +#ifdef NEED_GETGROUPLIST +int getgrouplist(GETGROUPLIST_ARGS); +#endif + +#ifdef POSIX_GETGRNAM_R +int +__posix_getgrnam_r(const char *, struct group *, char *, int, struct group **); +#endif + +#ifdef NEED_GETGRNAM_R +int +getgrnam_r(const char *, struct group *, char *, size_t, struct group **); +#endif + +#ifdef POSIX_GETGRGID_R +int +__posix_getgrgid_r(gid_t, struct group *, char *, int, struct group **) ; +#endif + +#ifdef NEED_GETGRGID_R +int +getgrgid_r(gid_t, struct group *, char *, size_t, struct group **); +#endif + +#ifdef NEED_GETGRENT_R +GROUP_R_RETURN getgrent_r(struct group *gptr, GROUP_R_ARGS); +#endif + +#ifdef NEED_SETGRENT_R +GROUP_R_SET_RETURN setgrent_r(GROUP_R_ENT_ARGS); +#endif + +#ifdef NEED_ENDGRENT_R +GROUP_R_END_RETURN endgrent_r(GROUP_R_ENT_ARGS); +#endif + +#if defined(NEED_INNETGR_R) && defined(NGR_R_RETURN) +NGR_R_RETURN +innetgr_r(const char *, const char *, const char *, const char *); +#endif + +#ifdef NEED_SETNETGRENT_R +#ifdef NGR_R_SET_ARGS +NGR_R_SET_RETURN setnetgrent_r(NGR_R_SET_CONST char *netgroup, NGR_R_SET_ARGS); +#else +NGR_R_SET_RETURN setnetgrent_r(NGR_R_SET_CONST char *netgroup); +#endif +#endif + +#ifdef NEED_ENDNETGRENT_R +#ifdef NGR_R_END_ARGS +NGR_R_END_RETURN endnetgrent_r(NGR_R_END_ARGS); +#else +NGR_R_END_RETURN endnetgrent_r(void); +#endif +#endif + +#ifdef POSIX_GETPWNAM_R +int +__posix_getpwnam_r(const char *login, struct passwd *pwptr, + char *buf, size_t buflen, struct passwd **result); +#endif + +#ifdef NEED_GETPWNAM_R +int +getpwnam_r(const char *login, struct passwd *pwptr, + char *buf, size_t buflen, struct passwd **result); +#endif + +#ifdef POSIX_GETPWUID_R +int +__posix_getpwuid_r(uid_t uid, struct passwd *pwptr, + char *buf, int buflen, struct passwd **result); +#endif + +#ifdef NEED_GETPWUID_R +int +getpwuid_r(uid_t uid, struct passwd *pwptr, + char *buf, size_t buflen, struct passwd **result); +#endif + +#ifdef NEED_SETPWENT_R +#ifdef PASS_R_ENT_ARGS +PASS_R_SET_RETURN setpwent_r(PASS_R_ENT_ARGS); +#else +PASS_R_SET_RETURN setpwent_r(void); +#endif + +#endif + +#ifdef NEED_SETPASSENT_R +#ifdef PASS_R_ENT_ARGS +PASS_R_SET_RETURN setpassent_r(int stayopen, PASS_R_ENT_ARGS); +#else +PASS_R_SET_RETURN setpassent_r(int stayopen); +#endif +#endif + +#ifdef NEED_GETPWENT_R +PASS_R_RETURN getpwent_r(struct passwd *pwptr, PASS_R_ARGS); +#endif + +#ifdef NEED_ENDPWENT_R +void endpwent_r(void); +#endif + +#ifdef NEED_SETPASSENT +int setpassent(int stayopen); +#endif + +#define gettimeofday isc__gettimeofday +#ifdef NEED_GETTIMEOFDAY +int isc__gettimeofday(struct timeval *tvp, struct _TIMEZONE *tzp); +#else +int isc__gettimeofday(struct timeval *tp, struct timezone *tzp); +#endif + +int getnetgrent(NGR_R_CONST char **machinep, NGR_R_CONST char **userp, + NGR_R_CONST char **domainp); + +#ifdef NGR_R_ARGS +int getnetgrent_r(NGR_R_CONST char **machinep, NGR_R_CONST char **userp, + NGR_R_CONST char **domainp, NGR_R_ARGS); +#endif + +/* setnetgrent and endnetgrent are defined in sunw_port_after.h +#ifdef SETNETGRENT_ARGS +void setnetgrent(SETNETGRENT_ARGS); +#else +void setnetgrent(const char *netgroup); +#endif + +void endnetgrent(void); +*/ + +#ifdef INNETGR_ARGS +int innetgr(INNETGR_ARGS); +#else +int innetgr(const char *netgroup, const char *machine, + const char *user, const char *domain); +#endif + +#ifdef NGR_R_SET_ARGS +NGR_R_SET_RETURN +setnetgrent_r(NGR_R_SET_CONST char *netgroup, NGR_R_SET_ARGS); +#else +NGR_R_SET_RETURN +setnetgrent_r(NGR_R_SET_CONST char *netgroup); +#endif + +#ifdef NEED_STRTOUL +unsigned long strtoul(const char *, char **, int); +#endif + +#ifdef NEED_SUN4PROTOS +#include <stdarg.h> +#ifndef __SIZE_TYPE__ +#define __SIZE_TYPE__ int +#endif +struct sockaddr; +struct iovec; +struct timeval; +struct timezone; +int fprintf(FILE *, const char *, ...); +int getsockname(int, struct sockaddr *, int *); +int getpeername(int, struct sockaddr *, int *); +int socket(int, int, int); +int connect(int, const struct sockaddr *, int); +int writev(int, struct iovec *, int); +int readv(int, struct iovec *, int); +int send(int, const char *, int, int); +void bzero(char *, int); +int recvfrom(int, char *, int, int, struct sockaddr *, int *); +int syslog(int, const char *, ... ); +int printf(const char *, ...); +__SIZE_TYPE__ fread(void *, __SIZE_TYPE__, __SIZE_TYPE__, FILE *); +__SIZE_TYPE__ fwrite(const void *, __SIZE_TYPE__, __SIZE_TYPE__, FILE *); +int fclose(FILE *); +int ungetc(int, FILE *); +int scanf(const char *, ...); +int sscanf(const char *, const char *, ... ); +int tolower(int); +int toupper(int); +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, int); +int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +#ifdef gettimeofday +#undef gettimeofday +int gettimeofday(struct timeval *, struct timezone *); +#define gettimeofday isc__gettimeofday +#else +int gettimeofday(struct timeval *, struct timezone *); +#endif +long strtol(const char*, char **, int); +int fseek(FILE *, long, int); +int setsockopt(int, int, int, const char *, int); +int bind(int, const struct sockaddr *, int); +void bcopy(char *, char *, int); +int fputc(char, FILE *); +int listen(int, int); +int accept(int, struct sockaddr *, int *); +int getsockopt(int, int, int, char *, int *); +int vfprintf(FILE *, const char *, va_list); +int fflush(FILE *); +int fgetc(FILE *); +int fputs(const char *, FILE *); +int fchown(int, int, int); +void setbuf(FILE *, char *); +int gethostname(char *, int); +int rename(const char *, const char *); +time_t time(time_t *); +int fscanf(FILE *, const char *, ...); +int sscanf(const char *, const char *, ...); +int ioctl(int, int, caddr_t); +void perror(const char *); + +#if !defined(__USE_FIXED_PROTOTYPES__) && !defined(__cplusplus) && !defined(__STRICT_ANSI__) +/* + * 'gcc -ansi' changes the prototype for vsprintf(). + * Use this prototype when 'gcc -ansi' is not in effect. + */ +char *vsprintf(char *, const char *, va_list); +#endif +#endif + +/* Solaris-specific changes */ +#include "sunw_port_after.h" + +#endif /* port_after_h */ diff --git a/usr/src/lib/libresolv2_joy/include/port_before.h b/usr/src/lib/libresolv2_joy/include/port_before.h new file mode 100644 index 0000000000..2801139223 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/port_before.h @@ -0,0 +1,201 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (C) 2005-2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: port_before.h.in,v 1.31 2008/02/28 05:36:10 marka Exp $ */ + +#ifndef port_before_h +#define port_before_h +/* Solaris-specific changes */ +#include "sunw_port_before.h" +#include <config.h> + +#ifdef NEED_SUN4PROTOS +#define _PARAMS(x) x +#endif + +struct group; /* silence warning */ +struct passwd; /* silence warning */ +struct timeval; /* silence warning */ +struct timezone; /* silence warning */ + +#ifdef HAVE_SYS_TIMERS_H +#include <sys/timers.h> +#endif +#include <limits.h> + +#ifdef ISC_PLATFORM_NEEDTIMESPEC +#include <time.h> /* For time_t */ +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif +#ifndef HAVE_MEMMOVE +#define memmove(a,b,c) bcopy(b,a,c) +#endif + +#undef WANT_IRS_GR +#undef WANT_IRS_NIS +#undef WANT_IRS_PW + +#define BSD_COMP 1 +#define USE_POLL 1 +#define HAVE_MD5 1 +#define SOLARIS2 1 + +/* DO_PTHREADS is conditionally defined in sunw_port_before.h + * #define DO_PTHREADS 1 */ +#define GETGROUPLIST_ARGS const char *name, gid_t basegid, gid_t *groups, int *ngroups +#define GETNETBYADDR_ADDR_T long +#define SETPWENT_VOID 1 +#define SETGRENT_VOID 1 + +#define NET_R_ARGS char *buf, int buflen +#define NET_R_BAD NULL +#define NET_R_COPY buf, buflen +#define NET_R_COPY_ARGS NET_R_ARGS +#define NET_R_END_RESULT(x) /*empty*/ +#define NET_R_END_RETURN void +#undef NET_R_ENT_ARGS /*empty*/ +#define NET_R_OK nptr +#define NET_R_RETURN struct netent * +#undef NET_R_SET_RESULT /*empty*/ +#undef NET_R_SETANSWER +#define NET_R_SET_RETURN void +#undef NETENT_DATA + +#define GROUP_R_RETURN struct group * +#define GROUP_R_SET_RETURN void +#undef GROUP_R_SET_RESULT /*empty*/ +#define GROUP_R_END_RETURN void +#define GROUP_R_END_RESULT(x) /*empty*/ +#define GROUP_R_ARGS char *buf, int buflen +#define GROUP_R_ENT_ARGS void +#define GROUP_R_OK gptr +#define GROUP_R_BAD NULL + +#define HOST_R_ARGS char *buf, int buflen, int *h_errnop +#define HOST_R_BAD NULL +#define HOST_R_COPY buf, buflen +#define HOST_R_COPY_ARGS char *buf, int buflen +#define HOST_R_END_RESULT(x) /*empty*/ +#define HOST_R_END_RETURN void +#undef HOST_R_ENT_ARGS /*empty*/ +#define HOST_R_ERRNO *h_errnop = h_errno +#define HOST_R_OK hptr +#define HOST_R_RETURN struct hostent * +#undef HOST_R_SETANSWER +#undef HOST_R_SET_RESULT +#define HOST_R_SET_RETURN void +#undef HOSTENT_DATA + +#define NGR_R_ARGS char *buf, int buflen +#define NGR_R_BAD (0) +#define NGR_R_COPY buf, buflen +#define NGR_R_COPY_ARGS NGR_R_ARGS +#define NGR_R_CONST +#define NGR_R_END_RESULT(x) /*empty*/ +#define NGR_R_END_RETURN void +#undef NGR_R_END_ARGS /*empty*/ +#define NGR_R_OK 1 +#define NGR_R_RETURN int +#define NGR_R_SET_CONST const +#undef NGR_R_SET_RESULT /*empty*/ +#define NGR_R_SET_RETURN void +#undef NGR_R_SET_ARGS + + +#if !defined(NGR_R_SET_ARGS) && defined(NGR_R_END_ARGS) +#define NGR_R_SET_ARGS NGR_R_END_ARGS +#endif + +#define PROTO_R_ARGS char *buf, int buflen +#define PROTO_R_BAD NULL +#define PROTO_R_COPY buf, buflen +#define PROTO_R_COPY_ARGS PROTO_R_ARGS +#define PROTO_R_END_RESULT(x) /*empty*/ +#define PROTO_R_END_RETURN void +#undef PROTO_R_ENT_ARGS /*empty*/ +#undef PROTO_R_ENT_UNUSED +#define PROTO_R_OK pptr +#undef PROTO_R_SETANSWER +#define PROTO_R_RETURN struct protoent * +#undef PROTO_R_SET_RESULT +#define PROTO_R_SET_RETURN void +#undef PROTOENT_DATA + +#define PASS_R_ARGS char *buf, int buflen +#define PASS_R_BAD NULL +#define PASS_R_COPY buf, buflen +#define PASS_R_COPY_ARGS PASS_R_ARGS +#define PASS_R_END_RESULT(x) /*empty*/ +#define PASS_R_END_RETURN void +#undef PASS_R_ENT_ARGS +#define PASS_R_OK pwptr +#define PASS_R_RETURN struct passwd * +#undef PASS_R_SET_RESULT /*empty*/ +#define PASS_R_SET_RETURN void + +#define SERV_R_ARGS char *buf, int buflen +#define SERV_R_BAD NULL +#define SERV_R_COPY buf, buflen +#define SERV_R_COPY_ARGS SERV_R_ARGS +#define SERV_R_END_RESULT(x) /*empty*/ +#define SERV_R_END_RETURN void +#undef SERV_R_ENT_ARGS /*empty*/ +#undef SERV_R_ENT_UNUSED /*empty*/ +#define SERV_R_OK sptr +#undef SERV_R_SETANSWER +#define SERV_R_RETURN struct servent * +#undef SERV_R_SET_RESULT +#define SERV_R_SET_RETURN void + + + +#define DE_CONST(konst, var) \ + do { \ + union { const void *k; void *v; } _u; \ + _u.k = konst; \ + var = _u.v; \ + } while (0) + +#define UNUSED(x) (x) = (x) + +#undef NEED_SOLARIS_BITTYPES +#define ISC_SOCKLEN_T int + +#ifdef __GNUC__ +#define ISC_FORMAT_PRINTF(fmt, args) \ + __attribute__((__format__(__printf__, fmt, args))) +#else +#define ISC_FORMAT_PRINTF(fmt, args) +#endif + +/* Pull in host order macros when _XOPEN_SOURCE_EXTENDED is defined. */ +#if defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED) +#include <sys/byteorder.h> +#endif + +#endif + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/port_netdb.h b/usr/src/lib/libresolv2_joy/include/port_netdb.h new file mode 100644 index 0000000000..a308cc7efa --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/port_netdb.h @@ -0,0 +1,188 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _PORT_NETDB_H +#define _PORT_NETDB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* AI_NUMERICSERV is not a valid flag for getaddrinfo */ +#define AI_MASK 0x0038 /* mask of valid flags */ + +/* EAI_OVERFLOW was removed from ISC */ +#define EAI_BADHINTS 12 + +/* + * these are libresolv2 functions that were renamed in previous versions to + * res_* because they conflict with libnsl or libsocket + */ + +#define endhostent joy_res_endhostent /* libnsl */ +void endhostent __P((void)); +#define endnetent res_endnetent /* libsocket */ +void endnetent __P((void)); +#define freeaddrinfo res_freeaddrinfo /* libsocket */ +void freeaddrinfo __P((struct addrinfo *)); +#define freehostent res_freehostent /* libsocket and libnsl */ +void freehostent __P((struct hostent *)); +#define getaddrinfo res_getaddrinfo /* libsocket */ +int getaddrinfo __P((const char *, const char *, + const struct addrinfo *, struct addrinfo **)); +#define gethostbyaddr joy_res_gethostbyaddr /* libnsl */ +struct hostent *gethostbyaddr __P((const char *, int, int)); +#define gethostbyname joy_res_gethostbyname /* libnsl */ +struct hostent *gethostbyname __P((const char *)); +#define gethostbyname2 joy_res_gethostbyname2 /* lib/nsswitch/dns */ +struct hostent *gethostbyname2 __P((const char *, int)); +#define gethostent res_gethostent /* libnsl */ +struct hostent *gethostent __P((void)); +#define getipnodebyaddr res_getipnodebyaddr /* libnsl and libsocket */ +struct hostent *getipnodebyaddr __P((const void *, size_t, int, int *)); +#define getipnodebyname res_getipnodebyname /* libnsl and libsocket */ +struct hostent *getipnodebyname __P((const char *, int, int, int *)); + +#define getnetbyaddr res_getnetbyaddr /* libsocket */ +struct netent *getnetbyaddr __P((unsigned long, int)); +#define getnetbyname res_getnetbyname /* libsocket */ +struct netent *getnetbyname __P((const char *)); +#define getnetent res_getnetent /* libsocket */ +struct netent *getnetent __P((void)); +#define sethostent joy_res_sethostent /* libnsl */ +void sethostent __P((int)); +#define setnetent res_setnetent /* libsocket */ +void setnetent __P((int)); + +/* + * these are other irs functions now included in libresolv.so.2. We rename the + * ones that overlap with libsocket or libnsl + */ + +/* endprotoent is in libsocket.so.1 */ +#define endprotoent res_endprotoent +void endprotoent __P((void)); + +/* endservent is in libsocket.so.1 */ +#define endservent res_endservent +void endservent __P((void)); + +/* note: the next two symbols are variables, not functions */ + +/* gai_errlist is in libsocket.so.1 */ +#define gai_errlist res_gai_errlist + +/* gai_nerr is in libsocket.so.1 */ +#define gai_nerr res_gai_nerr + +/* gai_strerror is in libsocket.so.1 */ +#define gai_strerror res_gai_strerror +const char *gai_strerror __P((int ecode)); + +/* gethostbyaddr_r is in libnsl.so.1 */ +#define gethostbyaddr_r res_gethostbyaddr_r +struct hostent *gethostbyaddr_r __P((const char *addr, int len, int type, + struct hostent *hptr, char *buf, + int buflen, int *h_errnop)); + +/* gethostbyname_r is in libnsl.so.1 */ +#define gethostbyname_r res_gethostbyname_r +struct hostent *gethostbyname_r __P((const char *name, struct hostent *hptr, + char *buf, int buflen, int *h_errnop)); + +/* gethostent_r is in libnsl.so.1 */ +#define gethostent_r res_gethostent_r +struct hostent *gethostent_r __P((struct hostent *hptr, char *buf, int buflen, + int *h_errnop)); + +/* getnameinfo is in libsocket.so.1 */ +#define getnameinfo res_getnameinfo +int getnameinfo __P((const struct sockaddr *, size_t, char *, + size_t, char *, size_t, int)); + +/* getnetbyaddr_r is in libsocket.so.1 */ +#define getnetbyaddr_r res_getnetbyaddr_r +struct netent *getnetbyaddr_r __P((long, int, struct netent *, char *, int)); + +/* getnetbyname_r is in libsocket.so.1 */ +#define getnetbyname_r res_getnetbyname_r +struct netent *getnetbyname_r __P((const char *, struct netent *, char *, int)); + +/* getnetent_r is in libsocket.so.1 */ +#define getnetent_r res_getnetent_r +struct netent *getnetent_r __P((struct netent *, char *, int)); + +/* getprotobyname is in libsocket.so.1 */ +#define getprotobyname res_getprotobyname +struct protoent *getprotobyname __P((const char *)); + +/* getprotobyname_r is in libsocket.so.1 */ +#define getprotobyname_r res_getprotobyname_r +struct protoent *getprotobyname_r __P((const char *, struct protoent *, + char *, int)); + +/* getprotobynumber is in libsocket.so.1 */ +#define getprotobynumber res_getprotobynumber +struct protoent *getprotobynumber __P((int)); + +/* getprotobynumber_r is in libsocket.so.1 */ +#define getprotobynumber_r res_getprotobynumber_r +struct protoent *getprotobynumber_r __P((int, + struct protoent *, char *, int)); + +/* getprotoent is in libsocket.so.1 */ +#define getprotoent res_getprotoent +struct protoent *getprotoent __P((void)); + +/* getprotoent_r is in libsocket.so.1 */ +#define getprotoent_r res_getprotoent_r +struct protoent *getprotoent_r __P((struct protoent *, char *, int)); + +/* getservbyname is in libsocket.so.1 and libnsl.so.1 */ +#define getservbyname res_getservbyname +struct servent *getservbyname __P((const char *, const char *)); + +/* getservbyname_r is in libsocket.so.1 and libnsl.so.1 */ +#define getservbyname_r res_getservbyname_r +struct servent *getservbyname_r __P((const char *name, const char *, + struct servent *, char *, int)); + +/* getservbyport is in libsocket.so.1 and libnsl.so.1 */ +#define getservbyport res_getservbyport +struct servent *getservbyport __P((int, const char *)); + +/* getservbyport_r is in libsocket.so.1 and libnsl.so.1 */ +#define getservbyport_r res_getservbyport_r +struct servent *getservbyport_r __P((int port, const char *, + struct servent *, char *, int)); + +/* getservent is in libsocket.so.1 */ +#define getservent res_getservent +struct servent *getservent __P((void)); + +/* getservent_r is in libsocket.so.1 */ +#define getservent_r res_getservent_r +struct servent *getservent_r __P((struct servent *, char *, int)); + +/* innetgr is in libsocket.so.1 */ +#define innetgr res_innetgr +int innetgr __P((const char *, const char *, const char *, const char *)); + +/* setprotoent is in libsocket.so.1 */ +#define setprotoent res_setprotoent +void setprotoent __P((int)); + +/* setservent is in libsocket.so.1 */ +#define setservent res_setservent +void setservent __P((int)); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _PORT_NETDB_H */ diff --git a/usr/src/lib/libresolv2_joy/include/port_resolv.h b/usr/src/lib/libresolv2_joy/include/port_resolv.h new file mode 100644 index 0000000000..cd1a97d40c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/port_resolv.h @@ -0,0 +1,42 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PORT_RESOLV_H +#define _PORT_RESOLV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* RES_NSID has the same value as RES_NO_NIBBLE, which has been deleted */ +#define RES_NSID 0x00040000 /* request name server ID */ + +/* RES_DEFAULT has a new value in libbind-6.0 */ +#undef RES_DEFAULT +#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | \ + RES_DNSRCH | RES_NO_NIBBLE2) + +#ifndef __ultrix__ +u_int16_t _getshort __P((const uchar_t *)); +u_int32_t _getlong __P((const uchar_t *)); +#endif + +/* rename functions so they can be wrapped (see sunw/sunw_wrappers.c */ +#define p_option isc_p_option +const char *p_option(ulong_t option); +#define p_secstodate isc_p_secstodate +char *p_secstodate(ulong_t secs); + +/* prevent namespace pollution */ +#define res_protocolnumber __res_protocolnumber +#define res_servicenumber __res_servicenumber + + + +#ifdef __cplusplus +} +#endif + +#endif /* _PORT_RESOLV_H */ diff --git a/usr/src/lib/libresolv2_joy/include/probe_ipv6 b/usr/src/lib/libresolv2_joy/include/probe_ipv6 new file mode 100755 index 0000000000..371ac96c55 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/probe_ipv6 @@ -0,0 +1,73 @@ +#!/bin/sh + +# Copyright 2003 by Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" + +set -e +PATH=/bin:/usr/bin:$PATH; export PATH +trap "rm -f tmp$$[abc].[oc]" 0 +target=port_ipv6 +new=new_${target}.h +old=${target}.h + +cat > tmp$$a.c <<EOF +#include <sys/types.h> +#include <netinet/in.h> +struct sockaddr_in6 xx; +EOF + +cat > tmp$$b.c <<EOF +#include <sys/types.h> +#include <netinet/in.h> +struct in6_addr xx; +EOF + +cat > tmp$$c.c <<EOF +#include <sys/types.h> +#include <netinet/in.h> +struct sockaddr_in6 xx; +main() { xx.sin6_scope_id = 0; } +EOF + +cat > ${new} <<EOF + +/* This file is automatically generated. Do Not Edit. */ + +#ifndef ${target}_h +#define ${target}_h + +EOF + +if ${CC} -c tmp$$a.c > /dev/null 2>&1 +then + echo "#define HAS_INET6_STRUCTS" >> ${new} + if ${CC} -c tmp$$b.c > /dev/null 2>&1 + then + : + else + echo "#define in6_addr in_addr6" >> ${new} + fi + if ${CC} -c tmp$$c.c > /dev/null 2>&1 + then + echo "#define HAVE_SIN6_SCOPE_ID" >> ${new} + else + echo "#undef HAVE_SIN6_SCOPE_ID" >> ${new} + fi +else + echo "#undef HAS_INET6_STRUCTS" >> ${new} +fi +echo >> ${new} +echo "#endif" >> ${new} +if [ -f ${old} ]; then + if cmp -s ${new} ${old} ; then + rm -f ${new} + else + rm -f ${old} + mv ${new} ${old} + fi +else + mv ${new} ${old} +fi +exit 0 diff --git a/usr/src/lib/libresolv2_joy/include/probe_ipv6.sh b/usr/src/lib/libresolv2_joy/include/probe_ipv6.sh new file mode 100644 index 0000000000..371ac96c55 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/probe_ipv6.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +# Copyright 2003 by Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" + +set -e +PATH=/bin:/usr/bin:$PATH; export PATH +trap "rm -f tmp$$[abc].[oc]" 0 +target=port_ipv6 +new=new_${target}.h +old=${target}.h + +cat > tmp$$a.c <<EOF +#include <sys/types.h> +#include <netinet/in.h> +struct sockaddr_in6 xx; +EOF + +cat > tmp$$b.c <<EOF +#include <sys/types.h> +#include <netinet/in.h> +struct in6_addr xx; +EOF + +cat > tmp$$c.c <<EOF +#include <sys/types.h> +#include <netinet/in.h> +struct sockaddr_in6 xx; +main() { xx.sin6_scope_id = 0; } +EOF + +cat > ${new} <<EOF + +/* This file is automatically generated. Do Not Edit. */ + +#ifndef ${target}_h +#define ${target}_h + +EOF + +if ${CC} -c tmp$$a.c > /dev/null 2>&1 +then + echo "#define HAS_INET6_STRUCTS" >> ${new} + if ${CC} -c tmp$$b.c > /dev/null 2>&1 + then + : + else + echo "#define in6_addr in_addr6" >> ${new} + fi + if ${CC} -c tmp$$c.c > /dev/null 2>&1 + then + echo "#define HAVE_SIN6_SCOPE_ID" >> ${new} + else + echo "#undef HAVE_SIN6_SCOPE_ID" >> ${new} + fi +else + echo "#undef HAS_INET6_STRUCTS" >> ${new} +fi +echo >> ${new} +echo "#endif" >> ${new} +if [ -f ${old} ]; then + if cmp -s ${new} ${old} ; then + rm -f ${new} + else + rm -f ${old} + mv ${new} ${old} + fi +else + mv ${new} ${old} +fi +exit 0 diff --git a/usr/src/lib/libresolv2_joy/include/res_update.h b/usr/src/lib/libresolv2_joy/include/res_update.h new file mode 100644 index 0000000000..0c6967db56 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/res_update.h @@ -0,0 +1,88 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1999 by Internet Software Consortium, Inc. + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* + * $Id: res_update.h,v 1.3 2005/04/27 04:56:15 sra Exp $ + */ + +#ifndef __RES_UPDATE_H +#define __RES_UPDATE_H + +/*! \file */ + +#include <sys/types.h> +#include <arpa/nameser.h> +#include <isc/list.h> +#include <resolv_joy.h> + +#ifndef ORIGINAL_ISC_CODE +/* definition of u_int32_t needed on Solaris */ +#include <sys/bitypes.h> +/* need to rename ns_updrec before we define it here */ +#include "arpa/port_nameser.h" +#endif /* ORIGINAL_ISC_CODE */ + + +/*% + * This RR-like structure is particular to UPDATE. + */ +struct ns_updrec { + LINK(struct ns_updrec) r_link, r_glink; + ns_sect r_section; /*%< ZONE/PREREQUISITE/UPDATE */ + char * r_dname; /*%< owner of the RR */ + ns_class r_class; /*%< class number */ + ns_type r_type; /*%< type number */ + u_int32_t r_ttl; /*%< time to live */ + u_char * r_data; /*%< rdata fields as text string */ + u_int r_size; /*%< size of r_data field */ + int r_opcode; /*%< type of operation */ + /* following fields for private use by the resolver/server routines */ + struct databuf *r_dp; /*%< databuf to process */ + struct databuf *r_deldp; /*%< databuf's deleted/overwritten */ + u_int r_zone; /*%< zone number on server */ +}; +typedef struct ns_updrec ns_updrec; +typedef LIST(ns_updrec) ns_updque; + +#ifdef ORIGINAL_ISC_CODE +#define res_mkupdate __res_mkupdate +#define res_update __res_update +#define res_mkupdrec __res_mkupdrec +#define res_freeupdrec __res_freeupdrec +#define res_nmkupdate __res_nmkupdate +#define res_nupdate __res_nupdate +#else +/* these are renamed in "port_nameser.h" */ +#endif /* ORIGINAL_ISC_CODE */ + + +int res_mkupdate __P((ns_updrec *, u_char *, int)); +int res_update __P((ns_updrec *)); +ns_updrec * res_mkupdrec __P((int, const char *, u_int, u_int, u_long)); +void res_freeupdrec __P((ns_updrec *)); +int res_nmkupdate __P((res_state, ns_updrec *, u_char *, int)); +int res_nupdate __P((res_state, ns_updrec *, ns_tsig_key *)); + +#endif /*__RES_UPDATE_H*/ + +/*! \file */ diff --git a/usr/src/lib/libresolv2_joy/include/resolv_mt.h b/usr/src/lib/libresolv2_joy/include/resolv_mt.h new file mode 100644 index 0000000000..500d4d764c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/resolv_mt.h @@ -0,0 +1,47 @@ +#ifndef _RESOLV_MT_H +#define _RESOLV_MT_H + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv_joy.h> + +/* Access functions for the libresolv private interface */ + +int __res_enable_mt(void); +int __res_disable_mt(void); + +/* Per-thread context */ + +typedef struct { +int no_hosts_fallback_private; +int retry_save; +int retry_private; +char inet_nsap_ntoa_tmpbuf[255*3]; +char sym_ntos_unname[20]; +char sym_ntop_unname[20]; +char p_option_nbuf[40]; +char p_time_nbuf[40]; +char precsize_ntoa_retbuf[sizeof "90000000.00"]; +char loc_ntoa_tmpbuf[sizeof +"1000 60 60.000 N 1000 60 60.000 W -12345678.00m 90000000.00m 90000000.00m 90000000.00m"]; +char p_secstodate_output[15]; +} mtctxres_t; + +/* Thread-specific data (TSD) */ + +mtctxres_t *___mtctxres(void); +#define mtctxres (___mtctxres()) + +/* Various static data that should be TSD */ + +#define sym_ntos_unname (mtctxres->sym_ntos_unname) +#define sym_ntop_unname (mtctxres->sym_ntop_unname) +#define inet_nsap_ntoa_tmpbuf (mtctxres->inet_nsap_ntoa_tmpbuf) +#define p_option_nbuf (mtctxres->p_option_nbuf) +#define p_time_nbuf (mtctxres->p_time_nbuf) +#define precsize_ntoa_retbuf (mtctxres->precsize_ntoa_retbuf) +#define loc_ntoa_tmpbuf (mtctxres->loc_ntoa_tmpbuf) +#define p_secstodate_output (mtctxres->p_secstodate_output) + +#endif /* _RESOLV_MT_H */ diff --git a/usr/src/lib/libresolv2_joy/include/sunw_port_after.h b/usr/src/lib/libresolv2_joy/include/sunw_port_after.h new file mode 100644 index 0000000000..bff64a74c1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/sunw_port_after.h @@ -0,0 +1,123 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SUNW_PORT_AFTER_H +#define _SUNW_PORT_AFTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * rename setnetgrent and endnetgrent which were formerly in a separate irs + * shared library. These functions should come from libc.so + */ +#define setnetgrent res_setnetgrent +#ifdef SETNETGRENT_ARGS +void setnetgrent(SETNETGRENT_ARGS); +#else +void setnetgrent(const char *netgroup); +#endif + +#define endnetgrent res_endnetgrent +void endnetgrent(void); + + +/* + * include ports for the public header files. ISC's versions are quite different + * from those currently in OpenSolaris. + */ + +#ifdef _RESOLV_JOY_H +#include <port_resolv.h> +#endif /* _RESOLV_JOY_H */ + +#ifdef _NETDB_H +#include <port_netdb.h> +#endif /* _NETDB_H */ + +#ifdef _ARPA_INET_H +#include <arpa/port_inet.h> +#endif /* _ARPA_INET_H */ + +#ifdef _ARPA_NAMESER_H +#include <arpa/port_nameser.h> +#endif /* _ARPA_NAMESER_H */ + + +#ifdef _ARPA_NAMESER_COMPAT_H +/* no changes */ +#endif /* _ARPA_NAMESER_COMPAT_H */ + +/* version-specific defines */ +#include <os_version.h> + +/* + * Prior to 2.6, Solaris needs a prototype for gethostname(). + */ +#if (OS_MAJOR == 5 && OS_MINOR < 6) +extern int gethostname(char *, size_t); +#endif +/* + * gethostid() was not available until 2.5 + * setsockopt(SO_REUSEADDR) fails on unix domain sockets before 2.5 + * use ioctl(FIONBIO) rather than fcntl() calls to set/clear non-blocking i/o. + */ +#if (OS_MAJOR == 5 && OS_MINOR < 5) +#define GET_HOST_ID_MISSING +#define NO_UNIX_REUSEADDR +#define USE_FIONBIO_IOCTL +#endif + +#if (OS_MAJOR == 5 && OS_MINOR < 11) +#define NEED_STRSEP +extern char *strsep(char **, const char *); +#endif + + +/* + * Solaris 2.5 and later have getrlimit(), setrlimit() and getrusage(). + */ +#if (OS_MAJOR > 5 || (OS_MAJOR == 5 && OS_MINOR >= 5)) +#include <sys/resource.h> +#define HAVE_GETRUSAGE +#define RLIMIT_TYPE rlim_t +#define RLIMIT_FILE_INFINITY +#endif + +/* the default syslog facility of named/lwresd. */ +#ifndef ISC_FACILITY +#define ISC_FACILITY LOG_DAEMON +#endif + + +/* + * Solaris 8 has if_nametoindex(). + */ +#if (OS_MAJOR > 5 || (OS_MAJOR == 5 && OS_MINOR >= 8)) +#define USE_IFNAMELINKID +#endif + +#undef ALIGN +#if (OS_MAJOR == 5 && OS_MINOR > 8) +#define ALIGN(x) (((uintptr_t)(x) + (sizeof (char *) - 1UL)) & \ + ~(sizeof (char *) - 1UL)) +#else +#define ALIGN(x) (((unsigned long)(x) + (sizeof (char *) - 1UL)) & \ + ~(sizeof (char *) - 1UL)) +#endif + +#if (OS_MAJOR == 5 && OS_MINOR < 5) +#ifndef USE_FIONBIO_IOCTL +#define USE_FIONBIO_IOCTL 1 +#endif +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _SUNW_PORT_AFTER_H */ diff --git a/usr/src/lib/libresolv2_joy/include/sunw_port_before.h b/usr/src/lib/libresolv2_joy/include/sunw_port_before.h new file mode 100644 index 0000000000..776e311fcc --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/sunw_port_before.h @@ -0,0 +1,43 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SUNW_PORT_BEFORE_H +#define _SUNW_PORT_BEFORE_H + +#ifdef SUNW_OPTIONS +#include <conf/sunoptions.h> +#endif + +/* version-specific defines */ +#include <os_version.h> +#if (OS_MAJOR == 5 && OS_MINOR < 6) +#ifndef SOLARIS_BITTYPES +#define NEED_SOLARIS_BITTYPES 1 +#endif +#endif + +#if (OS_MAJOR == 5 && OS_MINOR < 5) +#undef HAS_PTHREADS +#else +#define HAS_PTHREADS +#endif + +#if defined(HAS_PTHREADS) && defined(_REENTRANT) +#define DO_PTHREADS +#endif + +/* + * need these if we are using public versions of nameser.h, resolv.h, and + * inet.h + */ +#include <sys/param.h> +#if (!defined(BSD)) || (BSD < 199306) +#include <sys/bitypes.h> +#else +#include <sys/types.h> +#endif +#include <sys/cdefs.h> + +#endif /* _SUNW_PORT_BEFORE_H */ diff --git a/usr/src/lib/libresolv2_joy/include/sys/bitypes.h b/usr/src/lib/libresolv2_joy/include/sys/bitypes.h new file mode 100644 index 0000000000..54fb42bad7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/sys/bitypes.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2004, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1998, 1999, 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +/* $Id: bitypes.h,v 1.7 2008/11/14 02:54:35 tbox Exp $ */ + +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ + + /* + * Basic integral types. Omit the typedef if + * not possible for a machine/compiler combination. + */ + +#ifdef NEED_SOLARIS_BITTYPES + typedef /*signed*/ char int8_t; + typedef short int16_t; + typedef int int32_t; +#endif + typedef unsigned char u_int8_t; + typedef unsigned short u_int16_t; + typedef unsigned int u_int32_t; + +#endif /* __BIT_TYPES_DEFINED__ */ diff --git a/usr/src/lib/libresolv2_joy/include/sys/cdefs.h b/usr/src/lib/libresolv2_joy/include/sys/cdefs.h new file mode 100644 index 0000000000..67aac00cc7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/include/sys/cdefs.h @@ -0,0 +1,144 @@ +/* + * ++Copyright++ 1991, 1993 + * - + * Copyright (c) 1991, 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-- + */ + +/* + * @(#)cdefs.h 8.1 (Berkeley) 6/2/93 + * $Id: cdefs.h,v 1.2 2004/07/19 05:54:07 marka Exp $ + */ + +#ifndef _CDEFS_H_ +#define _CDEFS_H_ + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#define __const const /* define reserved names to standard */ +#define __signed signed +#define __volatile volatile +#if defined(__cplusplus) +#define __inline inline /* convert to C++ keyword */ +#else +#ifndef __GNUC__ +#define __inline /* delete GCC keyword */ +#endif /* !__GNUC__ */ +#endif /* !__cplusplus */ + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#ifndef __GNUC__ +#define __const /* delete pseudo-ANSI C keywords */ +#define __inline +#define __signed +#define __volatile +/* + * In non-ANSI C environments, new programs will want ANSI-only C keywords + * deleted from the program and old programs will want them left alone. + * When using a compiler other than gcc, programs using the ANSI C keywords + * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS. + * When using "gcc -traditional", we assume that this is the intent; if + * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone. + */ +#ifndef NO_ANSI_KEYWORDS +#define const /* delete ANSI C keywords */ +#define inline +#define signed +#define volatile +#endif +#endif /* !__GNUC__ */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC1 and some versions of GCC2 declare dead (non-returning) and + * pure (no side effects) functions using "volatile" and "const"; + * unfortunately, these then cause warnings under "-ansi -pedantic". + * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of + * these work for GNU C++ (modulo a slight glitch in the C++ grammar + * in the distribution version of 2.5.5). + */ +#if !defined(__GNUC__) || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) +#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#endif +#endif + +/* Delete pseudo-keywords wherever they are not available or needed. */ +#ifndef __dead +#define __dead +#define __pure +#endif + +#endif /* !_CDEFS_H_ */ diff --git a/usr/src/lib/libresolv2_joy/sparc/Makefile b/usr/src/lib/libresolv2_joy/sparc/Makefile new file mode 100644 index 0000000000..a333224278 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/sparc/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libresolv2_joy/sparcv9/Makefile b/usr/src/lib/libresolv2_joy/sparcv9/Makefile new file mode 100644 index 0000000000..ceed393e0d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/sparcv9/Makefile @@ -0,0 +1,38 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +# With the adition of BIND 8.3.3, the symbol table for 64 bit went over +# the limit for Kpic, so we've added -KPIC here, for just the 64 bit +# library. This avoids compiling the 32-bit library with PIC unnecessarily. + +sparcv9_C_PICFLAGS = -K PIC +sparcv9_CC_PICFLAGS = -KPIC + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libscf/inc/libscf.h b/usr/src/lib/libscf/inc/libscf.h index 773c0ca1d6..adc7455622 100644 --- a/usr/src/lib/libscf/inc/libscf.h +++ b/usr/src/lib/libscf/inc/libscf.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright 2016 RackTop Systems. * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ diff --git a/usr/src/lib/libsecdb/user_attr.txt b/usr/src/lib/libsecdb/user_attr.txt index aa31d7373b..227468d7c0 100644 --- a/usr/src/lib/libsecdb/user_attr.txt +++ b/usr/src/lib/libsecdb/user_attr.txt @@ -1,5 +1,6 @@ # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # # CDDL HEADER START # @@ -25,11 +26,12 @@ # user attributes. see user_attr(4) # # -root::::auths=solaris.*,solaris.grant;profiles=All;audit_flags=lo\:no;lock_after_retries=no;min_label=admin_low;clearance=admin_high -lp::::profiles=Printer Management adm::::profiles=Log Management -dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify +admin::::profiles=Primary Administrator;roles=root daemon::::auths=solaris.smf.manage.ilb,solaris.smf.modify.application +dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify +lp::::profiles=Printer Management netadm::::type=role;project=default;profiles=Network Autoconf Admin,Network Management,Service Management netcfg::::type=role;project=default;profiles=Network Autoconf User;auths=solaris.network.autoconf.write +root::::auths=solaris.*,solaris.grant;profiles=All;audit_flags=lo\:no;lock_after_retries=no;min_label=admin_low;clearance=admin_high zfssnap::::type=role;auths=solaris.smf.manage.zfs-auto-snapshot;profiles=ZFS File System Management diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c index 9f6acce19f..57118ce038 100644 --- a/usr/src/lib/libshare/nfs/libshare_nfs.c +++ b/usr/src/lib/libshare/nfs/libshare_nfs.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2014, 2016 by Delphix. All rights reserved. * Copyright 2018 Nexenta Systems, Inc. */ @@ -2789,9 +2790,16 @@ nfs_init() ret = initprotofromsmf(); if (ret != SA_OK) { - (void) printf(dgettext(TEXT_DOMAIN, - "NFS plugin problem with SMF repository: %s\n"), - sa_errorstr(ret)); + /* + * This is a workaround. See the comment in + * cmd/fs.d/nfs/lib/smfcfg.c for an explanation. + */ + if (getzoneid() == GLOBAL_ZONEID || + ret != SCF_ERROR_NOT_FOUND) { + (void) printf(dgettext(TEXT_DOMAIN, + "NFS plugin problem with SMF repository: %s\n"), + sa_errorstr(ret)); + } ret = SA_OK; } add_defaults(); diff --git a/usr/src/lib/libshell/Makefile b/usr/src/lib/libshell/Makefile index fd0a93421d..74fa80c6ea 100644 --- a/usr/src/lib/libshell/Makefile +++ b/usr/src/lib/libshell/Makefile @@ -63,6 +63,5 @@ $(SUBDIRS): FRC FRC: include Makefile.demo -include Makefile.doc include ../Makefile.targ diff --git a/usr/src/lib/libshell/Makefile.doc b/usr/src/lib/libshell/Makefile.doc deleted file mode 100644 index 5f507d28d9..0000000000 --- a/usr/src/lib/libshell/Makefile.doc +++ /dev/null @@ -1,77 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# - -ROOTDOCDIRBASE= $(ROOT)/usr/share/doc/ksh - -DOCFILES= \ - RELEASE \ - README \ - TYPES \ - DESIGN \ - COMPATIBILITY \ - OBSOLETE \ - shell_styleguide.docbook \ - images/tag_bourne.png \ - images/tag_i18n.png \ - images/tag_ksh88.png \ - images/tag_ksh93.png \ - images/tag_ksh.png \ - images/tag_l10n.png \ - images/tag_perf.png \ - images/callouts/1.png \ - images/callouts/2.png \ - images/callouts/3.png \ - images/callouts/4.png \ - images/callouts/5.png \ - images/callouts/6.png \ - images/callouts/7.png \ - images/callouts/8.png \ - images/callouts/9.png \ - images/callouts/10.png - -# Documentation rules -$(ROOTDOCDIRBASE)/%: common/% - $(INS.file) - -$(ROOTDOCDIRBASE)/%: misc/% - $(INS.file) - -ROOTDOCDIRS= \ - $(ROOTDOCDIRBASE) .WAIT \ - $(ROOTDOCDIRBASE)/images .WAIT \ - $(ROOTDOCDIRBASE)/images/callouts - -# Generic documentation rules -DOCFILESRCDIR= common -ROOTDOCFILES= $(DOCFILES:%=$(ROOTDOCDIRBASE)/%) -$(ROOTDOCDIRS) := OWNER = root -$(ROOTDOCDIRS) := GROUP = bin -$(ROOTDOCDIRS) := DIRMODE = 755 - -$(ROOTDOCDIRS): - $(INS.dir) - -install: $(ROOTDOCDIRS) .WAIT $(ROOTDOCFILES) diff --git a/usr/src/lib/libshell/common/COMPATIBILITY b/usr/src/lib/libshell/common/COMPATIBILITY deleted file mode 100644 index d4d645a99a..0000000000 --- a/usr/src/lib/libshell/common/COMPATIBILITY +++ /dev/null @@ -1,134 +0,0 @@ - - KSH-93 VS. KSH-88 - - -The following is a list of known incompatibilities between ksh-93 and ksh-88. -I have not include cases that are clearly bugs in ksh-88. I also have -omitted features that are completely upward compatible. - -1. Functions, defined with name() with ksh-93 are compatible with - the POSIX standard, not with ksh-88. No local variables are - permitted, and there is no separate scope. Functions defined - with the function name syntax, maintain compatibility. - This also affects function traces. - -2. ! is now a reserved word. As a result, any command by that - name will no longer work with ksh-93. - -3. The -x attribute of alias and typeset -f is no longer - effective and the ENV file is only read for interactive - shells. You need to use FPATH to make function definitions - visible to scripts. - -4. A built-in command named command has been added which is - always found before the PATH search. Any script which uses - this name as the name of a command (or function) will not - be compatible. - -5. The output format for some built-ins has changed. In particular - the output format for set, typeset and alias now have single - quotes around values that have special characters. The output - for trap without arguments has a format that can be used as input. - -6. With ksh-88, a dollar sign ($') followed by a single quote was - interpreted literally. Now it is an ANSI-C string. You - must quote the dollar sign to get the previous behavior. - Also, a $ in front of a " indicates that the string needs - to be translated for locales other than C or POSIX. The $ - is ignored in the C and POSIX locale. - -7. With ksh-88, tilde expansion did not take place inside ${...}. - with ksh-93, ${foo-~} will cause tilde expansion if foo is - not set. You need to escape the ~ for the previous behavior. - -8. Some changes in the tokenizing rules where made that might - cause some scripts with previously ambiguous use of quoting - to produce syntax errors. - -9. Programs that rely on specific exit values for the shell, - (rather than 0 or non-zero) may not be compatible. The - exit status for many shell failures has been changed. - -10. Built-ins in ksh-88 were always executed before looking for - the command in the PATH variable. This is no longer true. - Thus, with ksh-93, if you have the current directory first - in your PATH, and you have a program named test in your - directory, it will be executed when you type test; the - built-in version will be run at the point /bin is found - in your PATH. - -11. Some undocumented combinations of argument passing to ksh - builtins no longer works since ksh-93 is getopts conforming - with respect to its built-ins. For example, typeset -8i - previously would work as a synonym for typeset -i8. - -12. Command substitution and arithmetic expansion are now performed - on PS1, PS3, and ENV when they are expanded. Thus, ` and $( - as part of the value of these variables must be preceded by a \ - to preserve their previous behavior. - -13. The ERRNO variable has been dropped. - -14. If the file name following a redirection symbol contain pattern - characters they will only be expanded for interactive shells. - -15. The arguments to a dot script will be restored when it completes. - -16. The list of tracked aliases is not displayed with alias unless - the -t option is specified. - -17. The POSIX standard requires that test "$arg" have exit status - of 0, if and only if $arg is null. However, since this breaks - programs that use test -t, ksh93 treats an explicit test -t - as if the user had entered test -t 1. - -18. The ^T directive of emacs mode has been changed to work the - way it does in gnu-emacs. - -19. ksh-88 allowed unbalanced parenthes within ${name op val} whereas - ksh-93 does not. Thus, ${foo-(} needs to be written as ${foo-\(} - which works with both versions. - -20. kill -l in ksh-93 lists only the signal names, not their numerical - values. - -21. Local variables defined by typeset are statically scoped in - ksh93. In ksh88 they were dynamically scoped although this - behavior was never documented. - -22. The value of the variable given to getopts is set to ? when - the end-of-options is reached to conform to the POSIX standard. - -23. Since the POSIX standard requires that octal constants be - recongnized, doing arithmetic on typeset -Z variables can - yield different results that with ksh88. Most of these - differences were eliminated in ksh93o. - -24. Starting after ksh93l, If you run ksh name, where name does - not contain a /, the current directory will be searched - before doing a path search on name as required by the POSIX - shell standard. - -25. In ksh93, cd - will output the directory that it changes - to on standard output as required by X/Open. With ksh88, - this only happened for interactive shells. - -26. As an undocumented feature of ksh-88, a leading 0 to an - assignment of an integer variable caused that variable - to be treated as unsigned. This behavior was removed - starting in ksh93p. - -27. The getopts builtin in ksh93 requires that optstring contain - a leading + to allow options to begin with a +. - -28. In emacs/gmacs mode, control-v will not display the version when - the stty lnext character is set to control-v or is unset. - The sequence escape control-v will display the shell version. - -29. In ksh88, DEBUG traps were executed. after each command. In ksh93 - DEBUG traps are exeucted before each command. - -30. In ksh88, a redirection to a file name given by an empty string was - ignored. In ksh93, this is an error. -I am interested in expanding this list so please let me know if you -uncover any others. diff --git a/usr/src/lib/libshell/common/DESIGN b/usr/src/lib/libshell/common/DESIGN deleted file mode 100644 index c11c0aff1e..0000000000 --- a/usr/src/lib/libshell/common/DESIGN +++ /dev/null @@ -1,170 +0,0 @@ -Here is an overview of the source code organization for ksh93. - -Directory layout: - - The directory include contains header files for ksh93. - The files nval.h and shell.h are intended to be public - headers and can be used to add runtime builtin command. - The remainder are private. - - The directory data contains readonly data files for ksh93. - The man pages for built-ins are in builtins.c rather - than included as statics with the implementations in the - bltins directory because some systems don't make static const - data readonly and we want these to be shared by all running - shells. - - The directory edit contains the code for command line - editing and history. - - The fun directory contains some shell function such as - pushd, popd, and dirs. - - The directory features contains files that are used to generate - header files in the FEATURE directory. Most of these files - are in a format that is processed by iffe. - - The directory bltins contains code for most of the built-in - commands. Additional built-in commands are part of libcmd. - - The directory sh contains most of the code for ksh93. - - The directory tests contains a number of regression tests. - In most cases, when a bug gets fixed, a test is added to - one of these files. The regression tests can be run by - going to this directory and running - SHELL=shell_path shell_path shtests - where shell_path is an absolute pathname for the shell to - be tested. - - The top level directory contains the nmake Makefile, a README, - and several documentation files. The RELEASE file contains - the list of bug fixes and new features since the original - ksh93 release. The file COMPATIBILITY is a list of all - known incompatibilities with ksh88. - - The bash_pre_rc.sh is a startup script used when emulating - bash if the shell is compiled with SHOPT_BASH and the shell - is invoked as bash. The bash emulation is not complete. - -Include directory: - 1. argnod.h contains the type definitions for command - nodes, io nodes, argument nodes, and for positional - parameters.a It defines the prototypes for - all the positional parameters functions. - 2. builtins.h contains prototypes for builtins as well - as symbolic constants that refer to the name-pairs - that are associated with some of the built-ins. - It also contains prototypes for many of the strings. - 3. defs.h is the catch all for all definitions that - don't fit elsewhere and it includes several other - headers. It defines a strucuture that contains ksh - global data, sh, and a structure that contains per - function data, sh.st. - 4. edit.h contains definitions that are common to both - vi and emacs edit modes. - 5. env.h contains interfaces for creating and modifying - environment variables. - 6. fault.h contains prototypes for signal related - functions and trap and fault handling. - 7. fcin.h contains macro and function definitions for - reading from a file or string. - 8. history.h contains macros and functions definitions - related to history file processing. - 9. jobs.h contains the definitions relating to job - processing and control. - 10. lexstates.h contains the states associated with - lexical processing. - 11. name.h contains the internal definitions related - to name-value pair processing. - 12. national.h contains a few I18N definitions, mostly - obsolete. - 13. nval.h is the public interface to the name-value - pair library that is documented with nval.3. - 14. path.h contains the interface for pathname processing - and pathname searching. - 15. shell.h is the public interface for shell functions - that are documented int shell.3. - 16. shlex.h contains the lexical token definitions and - interfaces for lexical analysis. - 17. shnodes.h contains the definition of the structures - for each of the parse nodes and flags for the attributes. - 18. shtable.h contains some interfaces and functions for - table lookup. - 19. streval.h contains the interface to the arithmetic - functions. - 20. terminal.h is a header file that includes the appropriate - terminal include. - 21. test.h contains the definitions for the test and [[...]] - commands. - 22. timeout.h contains the define constant for the maximum - shell timeout. - 23. ulimit.h includes the appropriate resource header. - 24. variables.h contains symbolic constants for the built-in - shell variables. - -sh directory: - 1. args.c contains functions for parsing shell options - and for processing positional parameters. - 2. arith.c contains callback functions for the streval.c - library and the interface to shell arithmetic. - 3. array.c contains the code for indexed and associative - arrays. - 4. bash.h contains code used when compiling with SHOPT_BASH - to add bash specific features such as shopt. - 5. defs.c contains the data definitions for global symbols. - 6. deparse.c contains code to generate shell script from - a parse tree. - 7. env.c contains code to add and delete environment variables - to an environment list. - 8. expand.c contains code for file name expansion and - file name generation. - 9. fault.c contains code for signal processing, trap - handling and termination. - 10. fcin.c contains code for reading and writing a character - at a time from a file or string. - 11. init.c contains initialization code and callbacks - for get and set functions for built-in variables. - 12. io.o contains code for redirections and managing file - descriptors and file streams. - 13. jobs.c contains the code for job management. - 14. lex.c contains the code for the lexical analyzer. - 15. macro.c contains code for the $ macro expansions, including - here-documents. - 16. main.c contains the calls to initialization, profile - processing and the main evaluation loop as well as - mail processing. - 17. name.c contains the name-value pair routines that are - built on the hash library in libast. - 18. nvdisc.c contains code related to name-value pair disciplines. - 19. nvtree.c contains code for compound variables and for - walking the namespace. - 20. nvtype.c contains most of the code related to types that - are created with typeset -T. - 21. parse.c contains the code for the shell parser. - 22. path.c contains the code for pathname lookup and - some path functions. It also contains the code - that executes commands and scripts. - 23. pmain.c is just a calls sh_main() so that all of the - rest of the shell can be in a shared library. - 24. shcomp.c contains the main program to the shell - compiler. This program parses a script and creates - a file that the shell can read containing the parse tree. - 25. streval.c is an C arithmetic evaluator. - 26. string.c contains some string related functions. - 27. subshell.c contains the code to save and restore - environments so that subshells can run without creating - a new process. - 28. suid_exec.c contains the program from running execute - only and/or setuid/setgid scripts. - 29. tdump.c contains the code to dump a parse tree into - a file. - 30. timers.c contains code for multiple event timeouts. - 31. trestore contians the code for restoring the parse - tree from the file created by tdump. - 32. userinit.c contains a dummy userinit() function. - This is now obsolete with the new version of sh_main(). - 33. waitevent.c contains the sh_waitnotify function so - that builtins can handle processing events when the - shell is waiting for input or for process completion. - 34. xec.c is the main shell executuion loop. diff --git a/usr/src/lib/libshell/common/OBSOLETE b/usr/src/lib/libshell/common/OBSOLETE deleted file mode 100644 index c29cb8bb63..0000000000 --- a/usr/src/lib/libshell/common/OBSOLETE +++ /dev/null @@ -1,152 +0,0 @@ -.sp 3 -.tl ''Ksh Features That Are Obsolete in Ksh93'' -.sp 2 -.AL 1 -.LI -Using a pair of grave accents \^\fB\(ga\fR ... \fB\(ga\fR\^ -for command substition. Use \fB$(\fR ... \fB)\fR instead. -.LI -.B FCEDIT -is an obsolete name for -the default editor name for the -.B hist -command. -.B FCEDIT -is not used when -.B HISTEDIT -is set. Use -.B HISTEDIT -instead. -.LI -The newtest (\fB[[\fR ... \fB]]\fR) operator -\fB\-a\fP \fIfile\fP -is obsolete. Use -\fB\-e\fP instead. -.LI -The newtest (\fB[[\fR ... \fB]]\fR) operator -.BR = , -as used in -\fIstring\fP \fB=\fP \fIpattern\fP -is obsolete. Use -\fB==\fP instead. -.LI -The following obsolete arithmetic comparisons are also permitted: -.in +5 -.VL 20 -.LI "\fIexp1\fP \fB\-eq\fP \fIexp2\fP" -True, if -.I exp1 -is equal to -.IR exp2 . -.LI "\fIexp1\fP \fB\-ne\fP \fIexp2\fP" -True, if -.I exp1 -is not equal to -.IR exp2 . -.LI "\fIexp1\fP \fB\-lt\fP \fIexp2\fP" -True, if -.I exp1 -is less than -.IR exp2 . -.LI "\fIexp1\fP \fB\-gt\fP \fIexp2\fP" -True, if -.I exp1 -is greater than -.IR exp2 . -.LI "\fIexp1\fP \fB\-le\fP \fIexp2\fP" -True, if -.I exp1 -is less than or equal to -.IR exp2 . -.LI "\fIexp1\fP \fB\-ge\fP \fIexp2\fP" -True, if -.I exp1 -is greater than or equal to -.IR exp2 . -.LE \" End .VL -.in -5 -.LI -Using test -t or [ -t ] without specifying the file unit number. -.LI -The -.B \-k -option to the \fBset\fR builtin is obsolete. It causes -.I all\^ -variable assignment arguments are placed in the environment, -even if they occur after the command name. -The following -first prints -.B "a=b c" -and then -.BR c : -There is no alternative. -.LI -The obsolete -.B \-xf -option of the -.B typeset -command allows a function to be exported -to scripts that are executed without a separate -invocation of the shell. -Functions that need to be defined across separate -invocations of the shell should -be placed in a directory and the -.B FPATH -variable should contains the name of this directory. -They may also -be specified in the -.B ENV -file with the -.B \-xf -option of -.BR typeset . -.LI -The shell environment variable -.B FCEDIT -is obsolete. Use -.B HISTEDIT -instead. -.LI -In the -.B \-s -option -(to \fBfc\fR or \fBhist\fR command???) -( -and in obsolete versions, the editor name -.B \- -) -is used to skip the editing phase and -to re-execute the command. -.LI -The -.B \-t -option to \fBalias\fR builtin is is obsolete. It -is used to set and list tracked aliases. -There is no replacement. -.LI -The shell command line option -.B \-t -is obsolete. This option cause the shell to exit after reading -and executing one command. The is no replacement (although ending -\&"command" with the exit builtin should have the same effect). -.LI -As an obsolete feature of the "set" builtin, -if the first -.I arg\^ -is -.B \- -then the -.B \-x -and -.B \-v -options are turned off and the next -.I arg -is treated as the first argument. -Using -.B \+ -rather than -.B \- -causes these options to be turned off. -These options can also be used upon invocation of the shell. -.LE - diff --git a/usr/src/lib/libshell/common/README b/usr/src/lib/libshell/common/README deleted file mode 100644 index 3a953df356..0000000000 --- a/usr/src/lib/libshell/common/README +++ /dev/null @@ -1,232 +0,0 @@ -This directory, and its subdirectories contain the source code -for ksh-93; the language described in the second addition of -the book, "The KornShell Command and Programming Language," by -Morris Bolsky and David Korn which is published by Prentice Hall. -ksh-93 has been compiled and run on several machines with several -operating systems. The end of this file contains a partial list of -operating systems and machines that ksh-93 has been known to run on. - -The layout of files for ksh-93 has changed somewhat since ksh-88, -the last major release. Most of the source code for ksh remains in -the sh directory. However, the shell editing and history routines -are in the edit sub-directory. The code for shell built-ins is -in the bltins directory. The data directory contains read-only -data tables and messages that are used by the shell. The include -files remain in the include directory and the shlib directory -is gone. The features directory replaces the older install -directory. The method for generating systems specific feature -information has changed substantially. - -The Makefile file contains several compilation options that can be set -before compiling ksh. Options are of the form SHOPT_option and become -#define inside the code. These options are set to their recommended -value and some of these may disappear as options in future releases. -A value of 0, or no value represents off, 1 represents on. -Note that == is needed, not =, because these are nmake state variables -and changing their value will cause all modules that could be affected -by this change to be recompiled. -The options have the following defaults and meanings: - ACCT off Shell accounting. - ACCTFILE off Enable per user accounting info. - AUDIT off For auditing specific users - AUDITFILE "/etc/ksh_audit" - APPEND on Allows var+=val string and array append. - BASH off Bash compatibility mode. It is not fully implemented - and is experimental. - BRACEPAT on C-shell type abc{d,e}f style file generation - CMDLIB_BLTIN off Makes all commands in libcmd.a builtins. The - SH_CMDLIB_DIR nmake state variable can be used to - specify a directory. - CMDLIB_DIR off Sets CMDLIB_BLTIN=1 and provides a default value - of "/opt/ast/bin" for SH_CMDLIB_DIR. - COMPOUND_ARRAY - on Allows all components of compound variables except the - first to be any string by enclosing in [...]. It also - allows components other than the last to be arrays. - This is experimental and only partially complete. - CRNL off <cr><nl> treated as <nl> in shell grammar. - DYNAMIC on Dynamic loading of builtins. (Requires dlopen() interface.) - ECHOPRINT off Make echo equivalent to print. - ESH on Compile with emacs command line editing. The original - emacs line editor code was provided by Mike Veach at IH. - FILESCAN on Experimental option that allows fast reading of files - using while < file;do ...; done and allowing fields in - each line to be accessed as positional parameters. - FS_3D off For use with 3-D file system. Enabled automatically for - systems with dynamic linking. - KIA off Allow generation of shell cross reference database with -I. - MULTIBYTE on Multibyte character handling. Requires mblen() and - mbctowc(). - NAMESPACE on Allows namespaces. This is experimental, incomplete - and undocumented. - OLDTERMIO off Use either termios or termio at runtime. - OO on Experimental object oriented extension. This option - should disappear soon. - OPTIMIZE on Optimize loop invariants for with for and while loops. - P_SUID off If set, all real uids, greater than or equal to this - value will require the -p flag to run suid/sgid scripts. - PFSH off Compile with support for profile shell. - RAWONLY off Turn on if the vi line mode doesn't work right unless - you do a set -o viraw. - SEVENBIT off Strip the eigth bit from characters. - SPAWN off Use spawn as combined fork/exec. May improve speed on - some systems. - STATS on Add .sh.stats compound variable. - SUID_EXEC on Execute /etc/suid_exec for setuid, setgid script. - TIMEOUT off Set this to the number of seconds for timing out and - exiting the shell when you don't enter a command. If - non-zero, TMOUT can not be set larger than this value. - TYPEDEF on Enable typeset type definitions. - VSH on Compile with vi command line editing. The original vi - line editor code was provided by Pat Sullivan at CB. - -The following compile options are set automatically by the feature testing: - DEVFD Set when /dev/fd is a directory that names open files. - SHELLMAGIC - Set on systems that recognize script beginning with #! specially. - VPIX Set on systems the have /usr/bin/vpix program for running MS-DOS. - - -In most instances, you will generate ksh from a higher level directory -which also generates libcmd and libast libraries on which ksh depends. -However, it is possible to generate ksh, with by running make -f ksh.mk -in this directory. The ksh.mk file was generated from the nmake Makefile. -If you do not have make or nmake, but do have a Version 7 UNIX compatible -shell, then you can run the script mamexec < Mamfile to build ksh. -If you have nmake, version 2.3 or later, you can use it without the -f ksh.mk. -In either case, ksh relies on libraries libast and libcmd which must be -built first. The binary for ksh becomes the file named ./ksh which can -be copied to where ever you install it. - -If you use old make or the Mamfile, and you system has dynamic shared -libraries, then you should define the variables mam_cc_static and -mam_cc_dynanamic as the compiler options that request static linking -and dynamic linking respectively. This will decrease the number of -shared libraries that ksh need and cut startup time substantially. - -The makefile should also generate shcomp, a program that will precompile -a script. ksh93 is able to recognize files in this format and process -them as scripts. You can use shcomp to send out scripts when you -don't want to give away the original script source. - -It is advisable that you put the line PWD=$HOME;export PWD into the -/etc/profile file to reduce initialization time for ksh. - -To be able to run setuid/setgid shell scripts, or scripts without read -permission, the SUID_EXEC compile option must be on, and ksh must be installed -in the /bin directory, the /usr/bin directory, the /usr/lbin directory, -or the /usr/local/bin directory and the name must end in sh. The program -suid_exec must be installed in the /etc directory, must be owned by root, -and must be a suid program. If you must install ksh in some other directory -and want to be able to run setuid/setgid and execute only scripts, then -you will have to change the source code file sh/suid_exec.c explicitly. -If you do not have ksh in one of these secure locations, /bin/sh will -be invoked with the -p options and will fail when you execute a setuid/setgid -and/or execute only script. Note, that ksh does not read the .profile -or $ENV file when it the real and effective user/group id's are not -equal. - -The tests sub-directory contains a number of regression tests for ksh. -To run all these tests with the shell you just built, go to the tests -directory and run the command - SHELL=$dir/ksh $dir/ksh shtests -where dir is the directory of the ksh you want to test. - -The file PROMO.mm is an advertisement that extolls the virtues of ksh. -The file sh.1 contains the troff (man) description of this Shell. -The file nval.3 contains the troff (man) description of the name-value -pair library that is needed for writing built-ins that need to -access shell variables. - -The file sh.memo contains a draft troff (mm) memo describing ksh. The -file RELEASE88 contains the changes made for ksh88. The file RELEASE93 -contains the changes made in this release since ksh-88. The file -RELEASE contains bug fixes made in this release since ksh-88. The file -COMPATIBILITY contains a list of incompatibilities with ksh-88. The -file bltins.mm is a draft troff (mm) memo describing how to write -built-in commands that can be loaded at run time. - -Most of the work for internationalization has been done with ksh93. -The file ksh.msg is a generated file that contains error messages -that need to be translated. In addition, the function translate() -in sh/init.c has to be completed to interface with the dictionary -lookup. The translate function takes two argument, the string -that is to be translated and a type which is - 0 when a library string needs translation. - 1 when one of the error messages in ksh.msg needs translation. - 2 when a string in a script needs translation. You use a $ in front - of a double quoted string in a script to indicate that it - needs translation. The -D option for ksh builds the dictionary. -The translate routine needs to return the translated message. -For dictionaries that need to use a numeric key, it should be -possible to use the strhash() function to generate numbers to -go along with each of the messages and to use this number both -when generating the dictionary and when converting strings. -If you encounter error messages of type 1 that are not be translated via -this translate() function send mail to the address below. - -Please report any problems or suggestions to: - -dgk@research.att.com - - -ksh93 has been compiled and alpha tested on the following. An asterisk -signifies that ksh has been installed as /bin/sh on this machine. - -* Sun OS 4.1.[123] on sparc. - Sun OS 4.1.1 on sun. - Solaris 2.[1-9] on sparc. - Solaris 2.[4-8] on X86. - HP/UX 8 on HP-9000/730. - HP/UX 9 on HP-9000/730. - HP/UX 10 on HP-9000/857. - HP/UX 11 on pa-risc. - System V Release 3 on Counterpoint C19 - System V Release 4 on AT&T Intel 486. - System V Release 4 on NCR 4850 Intel 486. - IRIX Release 4.0.? System V on SGI-MIPS. - IRIX Release 5.1 System V on SGI-MIPS. - IRIX Release 6.[1-5] System V on SGI-MIPS. - System V Release 3.2 on 3B2. - UTS 5.2.6 on Amdahl 3090,5990,580. - System V Release 3.2 on i386. - SMP_DC.OSx olivetti dcosx MIServer-S 2/128. - SMP_DC.OSx Pyramid dcosx MIServer-S 2/160 r3000. - 4.3BSD on Vax 8650. - AIX release 2 on RS6000. - AIX 3.2 on RS6000. - Linux 1.X on Intel - Linux 2.X on Intel - Linux 2.X on Alpha - Linux 2.X on Alpha - Linux 2.X on OS/390 - Linux 2.X on sparc - Linux 2.4 on intel itanium 64 - Linux Slackware on sparc64 -* Linux ARM on i-PAQ - OSF1 on DEC alpha. - OSF4 on DEC alpha. - UMIPS 4.52 on mips. - BSD-i [2-4] on X86. - OpenBSD on X86 - NetBSD on X86 - FreeBSD on X86 - NeXT on Intel X86. - NeXT on HP. -* Windows NT using UWIN on X86 -* Windows NT using UWIN on alpha - Windows NT using Cygwin on X86 - Windows NT with NutCracker libraries. - Windows NT with Portage libraries. - Windows 3.1 using custom C library. - OpenEdition on MVS - Darwin OS X on PPC - MVS on OS 390 - SCO Openserver 3.2 on X86 - Unixware 7 on X86 - -Good luck!! - -David Korn -dgk@research.att.com - diff --git a/usr/src/lib/libshell/common/RELEASE b/usr/src/lib/libshell/common/RELEASE deleted file mode 100644 index b8fc92511a..0000000000 --- a/usr/src/lib/libshell/common/RELEASE +++ /dev/null @@ -1,2204 +0,0 @@ -10-03-05 --- Release ksh93t+ --- -10-03-05 A varibale unset memory leak has been fixed and tests/leaks.sh - has been added to verify the fix. -10-03-04 Documentation, comment, and disgnostic spelling typos corrected. -10-02-14 Fix sh_getenv() initialization to cooperate with the 3d fs. -10-02-12 A bug in which the get discipline function was not invoked for - associative array subscripts for unset array elements has been fixed. -10-02-12 A bug which could occur if the last line of a script was an eval - that executed multiple commands has been fixed. -10-02-02 A buffer overflow in read and another in binary type base64 - encoding were fixed. -10-01-20 A bug in the evaluation of arithmetic expression in which the - subscript was evaluated twice for $((foo[x++]++)) has been fixed. -10-01-19 A workaround for a double-free of a trap in both a subshell and its - parent has been added. -10-01-18 A bug in type handling of typeset -H has been fixed. -10-01-15 The "adding empty subscript" warning now only emitted with -x set. -10-01-01 A bug in the parser in which '$((case i in i):;esac);:))' was not - parsed correctly was fixed. -10-01-01 A bug in the parser in which '$(( 2 , 3.6 ))' dumped core for locales - with radix char , and thousands separator . has been fixed. -09-12-28 A bug in the handling of SIGCLD on systems that generated SIGCLD - while blocked waiting for process to complete has been fixed. -09-12-24 ast setlocale() reworked to differentiate env var changes from user - override. -09-12-18 A bug with the SHOPT_BGX option set which disabled traps for signals - < SIGCHLD when a trap for a signal > SIGCHLD was set has been fixed. -09-12-18 A bug where [[ -v var ]] was incorrect for some variables (including - LC_* vars) has been fixed. -09-12-15 A bug that produced a syntax error when a multibyte character - straddled a buffer boundary has been fixed. -09-12-11 A bug where the subscript of an unset variable was not evaluated has - been fixed. -09-12-09 A bug where shcomp dumped core on certain syntax errors has been fixed. -09-12-07 A bug where a parent shell environment var reset in a subshell removed - the value in subsequent children of the parent shell has been fixed. -09-12-04 A bug in which in some cases a trap in a function executed in - a subshell could trigger twice has been fixed. -09-12-03 A bug in which SHLVL exported with some attributes could cause - the shell to abort at startup has been fixed. -09-12-02 A bug with pipefail in which the shell could hang waiting for the - writer to complete before the last reader command has been fixed. -09-11-30 A bug in which a trap could be inherited by the first element of - a pipeline when the command had more than 63 arguments that did - not contain any macro expansions has been fixed. -09-11-19 When read from a terminal was called from with a while or foo loop, - and an edit mode was on, a backspace or erase no longer will - overwrite the prompt. -09-11-17 Change .paths parse to handle BUILTIN_LIB=foo BUILTIN_LIB=foo-1.2. -09-11-17 Inside a function, typeset foo.bar will bind foo to global variable - foo if local variable foo does not exist, instead of creating a - local variable. -09-11-17 "read -n1" from the terminal has been fixed to read exactly one character. -09-11-11 Job control now works for subshell commands, (...). -09-11-11 If set -e is on for an interactive shell errors in special builtins - now cause the shell to exit. -09-11-11 A bug in which an interrupt handler processed during the read builtin - when IFS did not contain a new line has been fixed. -09-11-09 A bug in which a variable that has been unset in a subshell and then - exported from that subshell does not show up in the environment - has been fixed. -09-11-02 ``,2'' is now a valid numeric constant for locales with - decimal_point=','. -09-11-02 A bug where "return" in .profile did not restore the shell state - has been fixed. -09-10-31 A bug that corrupted saved exit status when pids wrapped around has - been fixed. -09-10-26 A bug in { LANG LC_ALL LC_category } ordering has been fixed in -last. -09-10-16 A bug where notification to libast that the environment has changed - has been fixed. -09-10-12 A bug in which a function loaded in a subshell could leave side - effects in the parent shell has been fixed. -09-10-12 A bug in converting a printf %d operand to a number when the operand - contains multiple subscripts for the same variable has been fixed. -09-10-09 A bug in the handling of the escape character \ in directory prefixes - in command completion has been fixed. -09-10-09 $PATH processing has been changed to delay dir stat() and .paths - lookup until the directory is needed in the path search. -09-09-28 Call the ast setlocale() intercept on unset too. -09-09-24 A bug in which LANG=foo; LC_ALL=foo; unset LC_ALL; did not revert - LC_CTYPE etc. to the LANG value has been fixed. -09-09-17 A bug in which unsetting SVLVL could cause a script invoked by - name without #! to core dump has been fixed. -09-09-16 A bug in which a pipeline in a here-document could hang when the - pipefail option was on has been fixed. -09-09-09 A bug in the processing of line joining in here documents which - occurred when a buffer began with <escape><new-line> has been fixed. -09-09-09 A leading ; with commands in a brace group or parenthesis group - no longer causes an error. It now is used for the "showme" option. -09-09-09 A bug in which a subshell containing a background process could - block until the background process completed has been fixed. -09-09-04 A bug in handing ${var[sub]}, where var is a nameref has been fixed. -09-09-03 A bug which caused an index array to have the wrong number of elements - when it was converted from a compound variable by adding an another - element has been fixed. -09-09-03 Specifying export for a compound variable now generates an error. -09-09-02 $"..." localizations strings are no longer recognized inside `...`. -09-09-01 A bug in the for loop optimizer in the handling of type static - variables has been fixed. -09-09-01 An error message is not displayed when * and @ are used as subscripts. -09-09-01 Several bugs in the processing for types that included an associative - array of another type has been fixed. -09-09-01 A bug in the tracing of [[ a < b ]] and [[ a > b ]] has been fixed. -09-08-26 The .sh.file variable was not being set for a script that was run - by name and didn't start with #! and this has been fixed. -09-08-25 A bug in which a function called to deeply from command substitution - did not display an error message has been fixed. -09-08-24 When processing profiles, ksh93 now violates the POSIX standard and - treats &> as a redirection operator similar to bash. -09-08-23 A bug in the handling of the trap on SIGPIPE that could lead to a - memory fault has been fixed. -09-08-21 A bug in the handling of the comma operator in arithmetic expressions - that could cause a core dump on some systems has been fixed. -09-08-20 A bug in which a compound variable containing an array of a type - that doesn't have any elements now expands correctly. -09-08-19 A bug which disabled function tracing inside a function after - a call to another function has been fixed. -09-08-19 A bug in which initializing a compound variable instance to another - compound variable by name has been fixed. -09-08-18 A bug in which compound variable instances could be lost after - an instance that invoked a type method discipline has been fixed. -09-08-18 A bug in which a discipline function for a type applied to an - array instance when invoked in a function ignored the subscript - has been fixed. -09-08-18 A scoping error with variables in arithmetic expression with - type variables when reference with a name reference has been fixed. -09-08-10 Several memory leaks were fixed primarily related to subshells. -09-08-06 A bug in which setting the trap on CHLD to ignore could cause - a script to hang has been fixed. -09-07-08 A bug in the processing of name reference assignments when it - contained pattern expansions with quoting has been fixed. -09-06-22 The default width for typeset -X has been changed so that there - should be no loss of precision when converting to a string. -09-06-19 A bug in the printing of array elements for binary variables with - printf %B has been fixed. -09-06-19 A bug which caused a core dump with trap DEBUG set with an array - assignment with no elements has been fixed. -09-06-19 A bug with read with typeset -b -Z<num> has been fixed. -09-06-19 Two bugs related to read -b for array variables has been fixed. -09-06-19 A bug with typeset for compound variables containing arrays of - compound variables has been fixed. -09-06-18 A bug in appending a compound variable to a an indexed array of - compound variables has been fixed. -09-06-18 A bug which occurs when appending a compound variable to an indexed - array element has been fixed. -09-06-18 Setting VISUAL to a value other than one ending in vi or emacs will - no longer unset the edit mode. -09-06-17 A bug in typeset -m when moving a local compound variable to a - global compound variable via a name reference has been fixed. -09-06-17 A bug in appending to nodes of an array of compound variables when - addressing them via nameref has been fixed. -09-06-17 A bug in typeset -p var, when var is an array of compound variables - in which the output only contained on array element has been fixed. -09-06-17 The prefix expansion ${!y.@} now works when y is a name - reference to an element of an array. -09-06-16 Traps on signals that are ignored when the shell is invoked - no longer display. Previously they were ignored as required but - would be listed with trap -p. -09-06-12 A bug in vi edit mode in which hitting the up arrow key at the - end of a line longer than 40 characters which caused a core dump - has been fixed. -09-06-11 A bug in which "eval non-builtin &" would create two processes, - one for the & and another for non-builtin has been fixed. -09-06-08 When var is an identifier and is unset, ${var} no longer tries to - run command substitution on the command var. -09-06-08 Process substitution arguments of the form <(command) can now be - used following the < redirection operator to redirect from command. -09-05-13 A bug in which redirections of the form 2>&1 1>&5 inside command - substitution could cause the command substitution to hang has been - fixed. -09-05-12 To conform with POSIX, the -u option only checks for unset variables - and subscript elements rather than checking for all parameters. -09-05-12 A bug which could cause a core dump when a variable whose name - begins with a . was referenced as part of a name reference inside - a function has been fixed. -09-05-01 A bug that caused a core dump when SIGWINCH was received and - both vi and emacs mode were off has been fixed. -09-04-22 Default alias compound='typeset -C' added. -09-04-15 A bug that caused ${...;} to hang for large files has been fixed. -09-04-08 A change was made in the -n option which printed out an incorrect - warning with <>. -09-04-07 The emacs edit command M-_ and M_. and the vi command _ was fixed - to handle the case there there is no history file. -09-04-05 A bug in handling new-lines with read -n has been fixed. -09-04-05 The ENV variable defaults the the file named by $HOME/.kshrc rather - then to the string $HOME/.kshrc. -09-03-31 A bug in which a nested command substitution with redirections could - leave a file descriptor open has been fixed. -09-03-24 ksh now only uses the value of the _ variable on startup if it can - verify that it was set by the invoking process rather than being - inherited by some other ancestor. -09-03-24 When ksh is invoked without -p and ruid!=euid, and the shell is - compiled without SHOPT_P_UID or ruid<SHOPT_P_UID, the shell now - enables the -p option. The previous version instead set the - euid to the ruid as it does for set +p. -09-03-24 When SHOPT_P_UID is defined at compile time and the shell is started - without -p and ruid!=euid and ruid>=SHOPT_P_UID then euid is set - to ruid. A bug that did the wrong test (ruid<SHOPT_P_UID) was fixed. -09-03-17 The sleep(1) builtin now accept and ISO 8601 PnYnMnDTnHnMnS - duration or date(1) compatible date/time operand. -09-03-10 If a variable that was left or right justified or zero-filled was - changed with a typeset statement that was left or right justified - or zero-filled, then the original justification no longer affects - the result. -09-03-10 A bug in the handling of traps when the last command in a script - is a subshell grouping command has been fixed. -09-03-03 A bug in which an expansion of the form ${!prefix@} could generate - an exception after the return from a function has been fixed. -09-02-02 A bug in restricted mode in which the value of ENV could be - changed from within a function has been fixed. -09-02-02 A bug in which an erroneous message indicating that a process - terminated with a coredump has been fixed. -09-02-02 The exit status when exit was called without an argument from - a signal handler was incorrect and has been fixed. -09-02-02 A bug in which a function autoloaded in a subshell could cause - a core dump when the subshell completed has been fixed. -09-02-02 A bug in which 2>&1 inside a command substitution wasn't working - correctly has been fixed. -09-02-02 A bug in the call stack of arithmetic function with 2 args - returning int has been fixed. -09-01-30 A bug in which 'eval print \$0' inside a function was giving the - wrong value for $0 has been fixed. -09-01-28 A bug in which a command substitution could return an exit status - of 127 when the pipefail option is enabled has been fixed. -09-01-26 ksh93 now generates an error message if you attempt to create - a global name reference to a local variable. -09-01-26 The [[ -v var ]] operator was modified to test for array elements. -09-01-23 The redirection operator <>; was added. It is similar to <> - except that if the command it is applied to succeeds, the file - is truncated to the offset at the command completion. -09-01-23 The default file descriptor for <> was changed to 1. -09-01-20 A bug in which the exit status specified in an exit trap was - not used when a process terminated with a signal has been fixed. -09-01-19 A bug in which a signal whose default action is to terminate - a process could be ignored when the process is running a sub-shell - has been fixed. -09-01-19 A bug in which sending SIGWINCH to a process that reads from a pipe - could cause a memory fault has been fixed. -09-01-16 The -R unary operator was added to [[...]] and test to check whether - a variable is a name reference. -09-01-16 The -v unary operator was added to [[...]] and test to check whether - a variable is set. -09-01-14 The unset built-in was modified to return 0 exit status when - unsetting a variable that was unset to conform with the POSIX - standard. -09-01-14 The unset built-in was modified to continue to unset variables - after encountering a variable that it could not unset to - conform to the POSIX standard. -09-01-14 The parameter expansion ${x+value} no longer expands the value of - the variable x when determining whether x is set or not. -09-01-13 A bug in which background jobs and pipelines that were not waited - for could, in rare instances, cause the shell to go into an infinite - loop or fail has been fixed. -09-01-06 A bug in indexed arrays of compound variables in which referencing - non-existent sub-variable in an arithmetic expression could cause - the sub-variable to be created has been fixed. -09-01-05 A bug in which the \ character did not escape extended regular - expression pattern characters has been fixed. -08-12-24 A bug in which killing the last element of a pipe did not cause - a write to the pipe to generate a SIGPIPE has been fixed. -08-12-19 A bug which could cause command substitution to hang when the - last element of a pipeline in a command substitution was a built-in - and the output was more that PIPE_BUFF. -08-12-18 A bug which occurs when a here documented marker embedded in a - command substitution occurs on a buffer boundary has been fixed. -08-12-17 A bug in the output of typeset -p for variables that had attributes - but did not have a value has been fixed. -08-12-16 A bug in which a name reference to a name reference variable that - references an array element has been fixed. -08-12-16 A bug in which a variable given both the -A and -C attribute along - with an initial assignment didn't work correctly has been fixed. -08-12-10 The [[ -t fd ]] test was fixed to handle fd>9. -08-12-10 A bug where function stack misalignment could cause a bus error - has been fixed. -08-12-09 Command completion was changed to use \ to quote special characters - instead of quoting the argument in single quotes. -08-12-07 A bug in typeset -m which occurred when the target node was an - associative array element has been fixed. -08-12-07 A timing bug on some systems (for example darwin), that could - cause the last process of a pipeline entered interactively to fail - with an "Exec format error" has been fixed. -08-12-04 SHOPT_BGX enables background job extensions. Noted by "J" in - the version string when enabled. (1) JOBMAX=n limits the number - of concurrent & jobs to n; the n+1 & job will block until a - running background job completes. (2) SIGCHLD traps are queued - so that each completing background job gets its own trap; $! is - set to the job pid and $? is set to the job exit status at the - beginning of the trap. (3) sleep -s added to sleep until the time - expires or until a signal is delivered. -08-12-04 The sign of floating point zero is preserved across arithmetic - function calls. -08-12-04 A bug that caused print(1) to produce garbled stdout/stderr - output has been fixed. -08-12-04 A bug in which printf "%d\n" "'<euro>'" did not output the - numerical value of the EURO symbol, 8354, has been fixed. -08-11-24 /dev/fd* and /dev/std* redirections are first attempted with - open() to preserve seek semantics; failing that the corresponding - file descriptors are dup()'d. -08-11-20 A bug which could cause a core dump if a function compiled with - shcomp was found has been fixed. -08-11-20 A bug in which jobs were not cleared from the jobs table for - interactive shells when the pipefail option is on has been fixed. -08-11-11 A bug in which a field that was unset in a type definition and later - set for an instance could appear twice when displaying the variable - has been fixed. -08-11-11 A bug in which running a simple command & inside a function would - not return the correct process id has been fixed. -08=11-10 A bug in which the exit status of a command could be lost if the pid - was that of the most recent command substitution that had completed - has been fixed. -08-11-10 The maximum depth for subshells has been increased from 256 to 65536. -08-11-06 A bug which could cause a core dump when the _ reference variable was - used as an embedded type with a compound assignment has been fixed. - -08-10-31 --- Release ksh93t --- -08-10-31 Variable scoping/initialization bugs that could dump core were fixed. -08-10-24 The lexer now accepts all RE characters for patterns prefixed - with a ksh ~(...) option expression. -08-10-24 For ${var/pat/sub} \0 in sub expands to the text matched by pat. -08-10-18 A bug in array scoping that could dump core has been fixed. -08-10-10 read -n and -N fixed to count characters in multibyte locales. -08-10-10 A bug that mishandled _.array[] type references has been fixed. -08-10-09 ${.sh.version} now contains a concatenation of the following (after - 'Version') denoting compile time features: - A SHOPT_AUDIT - B SHOPT_BASH - L SHOPT_ACCT - M SHOPT_MULTIBYTE -08-10-09 A bug that caused subshell command substitution with redirection - to hang has been fixed. -08-10-08 Output errors, other than to stderr, now result in a diagnostic. -08-10-08 ksh93 now supports types that contain arrays of other types as - members. Earlier versions core dumped in this case. -08-10-05 A bug which caused the shell to emit a syntax error for an arithmetic - statement of the form (( var.name[sub] = value)) has been fixed. -08-10-01 A bug that caused subshell command substitution to hang has - been fixed. -08-09-29 When the -p export option of typeset is used with other options, - only those variables matching the specified options are displayed. -08-09-29 When the shell reads the environment and finds variables that are - not valid shell assignments, it now passes these on to subsequent - commands rather than deleting them. -08-09-29 A bug in the display of compound variables containing an indexed - array of compound variables has been fixed. -08-09-29 A bug in the display of compound variables containing an associative - array with a subscript containing a . in the name has been fixed. -08-09-26 A core dump in the subshell environment restore has been fixed. -08-09-24 $(...) has been fixed to properly set the exit status in $?. -08-09-23 $(<...) with IFS=$'\n\n' has been fixed to retain all but the last - of multiple trailing newlines. -08-09-23 The -p option to typeset when used with other attributes, restricts - the output to variables with the specified attributes. -08-09-22 A bug that sometimes lost the exit status of a job has been fixed. -08-09-21 A bug that retained trailing command substitution newlines in - cases where the command caused the shell to fork has been fixed. -08-09-19 type, whence -v, and command -v were fixed to comply with POSIX - by writing 'not found' diagnostics to the standard error. -08-09-18 test and [...] were fixed to comply with POSIX in the case - of test '(' binop ')' where binop is a valid binary test operator. -08-09-16 If a method discipline named create is specified when defining a - type, this function will be called when an instance is created. -08-09-15 The variable _ is now set as a reference to the compound variable - when defining a compound variable or a type. -08-09-10 The shell now prints an error message when the type name specified - for an indexed array subscript is not an enumeration type. -08-09-10 A bug in which a subshell that spawned a background process could - loose output that was produced after the foreground completed - has been fixed. -08-09-10 A timing bug on some systems that could cause coprocesses started by a - subshell to not clean up and prevent other coprocesses has been fixed. -08-09-09 The typeset -m option is now able to rename array elements from - the same array. -08-09-09 The exit status of 2 from the DEBUG trap causes the next command - to be skipped. An exit value of 255 from a DEBUG trap called from - a function causes the function to return. -08-09-08 A bug in which a coprocess created in a subshell that did not - complete when the subshell terminated could prevent a coprocess - from being created in the parent shell has been fixed. -08-09-05 An assignment of the form name1=name2 where name1 and name2 - are both compound variables causes name1 to get a copy of name2. - name1+=name2 causes name2 sub-variables to be appended to name1. -08-09-05 A bug in which unsetting a compound variable did not unset all - the sub-variables has been fixed. -08-09-01 A bug in the subshell cleanup code that could cause SIGSEGV has - been fixed. -06-08-26 The SHLVL variable which is an environment variable used by bash - and zsh that gets incremented when the shell starts. -08-08-25 For an indexed array, a negative subscript now refers to offsets - from the end so that -1 refers to the last element. -08-08-24 An alignment error for shorts on 64 bit architectures has been fixed. -08-08-22 If oldvar is a compound variable, typeset -C newvar=oldvar creates - newvar as a copy of oldvar. -08-08-19 The ALRM signal no longer cause the sleep builtin to terminate. -08-08-13 When used in an arithmetic expression, the .sh.version variable - now produces a number that will be increasing for each release. -08-08-11 A bug in which type instantiation with a compound assignment in - a dot script in which the type is defined has been fixed. -08-08-07 The -m option has been added to typeset to move or rename a - variable. Not documented yet. -08-08-06 A bug in read when used in a loop when a prompt was specified - when reading from a terminal has been fixed. -08-08-01 A bug with the pipefail option in which a nested pipeline could - cause an asynchronous command to block has been fixed. -08-08-01 A for loop optimizer bug that treats .sh.lineno as an invariant - has been fixed. -08-07-30 A bug in which expanding compound variable that had a get discipline - from with a here document could cause a syntax error has been fixed. -08-07-18 A bug in which a nameref caused a local variable to be created - rather than binding to an existing variable in the global scope - has been fixed. -08-07-17 A bug which occurred when a nameref was created from within a - function that was part of a pipeline has been fixed. -08-07-14 The compile option SHOPT_STATS was added. With this option the - compound variable .sh.stats keeps usage statistics that could help - with performance tuning. -08-07-10 The output of set now always uses a single line for each variable. - For array variables, the complete set of values is now displayed. -08-07-09 The typeset -C option can be used with arrays to indicate that - each element should default to a compound variable. -08-07-08 The %B format now outputs compound variables and arrays. The - alternate flag # can be used to cause output into a single line. -08-07-03 When the window change signal, WINCH, is received, the current - edit line is redrawn in place. -08-07-01 A bug in the handling of shared variables when inside an embedded - type has been fixed. -08-06-29 A bug in multiline edit mode which occurred when the prompt length - was three characters or less has been fixed. -08-06-23 A bug in which the SIGCLD was not be triggered when background - jobs completed has been fixed. -08-06-23 KSH_VERSION added as a name reference to .sh.version. -08-06-20 type now outputs 'special builtin' for special builtins. -08-06-19 A couple of bugs in multi-dimensional arrays have been fixed. -08-06-19 A bug in which a syntax error in a dot script could generated - a syntax error in the next subsequent command has been fixed. -08-06-17 Reduced the maximum function call depth to 2048 to avoid exceptions - on some architectures. -08-06-16 A bug in which printf "%B" could generate an exception when the - specified variable was not set has been fixed. -08-06-16 When typeset -p is followed by variable names, it now displays - the attributes names and values for the specific names. -08-06-14 A bug that could effect the drawing of the screen from multiline - emacs or gmacs mode when walking up the history file has been fixed. -08-06-13 A bug in which a compound variable defined in a subshell could - have side effects into the parent shell has been fixed. -08-06-13 A number of bugs related to using .sh.level to set the stack from - for DEBUG traps have been fixed. -08-06-13 The .sh.lineno variable has been added. When .sh.level is changed - inside a DEBUG trap, the .sh.lineno contains the calling line number - for the specified stack frame. -08-06-13 The .sh.level variable has been documented and now works. -08-06-11 The -C option has been added to read for reading compound command - definitions from a file. -08-06-11 The . command is now permitted inside a compound command definition. - The dot script can contain declaration commands and dot commands. -08-06-09 Add -C option to typeset so that typeset -C foo, is equivalent - to foo=(). -08-06-09 Added -n warning message for typeset option orderings that are valid - with ksh88 but not valid with ksh93, for example Lx5. -08-06-09 A bug in which the return value for an assignment command containing - a command substitution with that failed was zero when the assignment - contained redirections has been fixed. -08-06-09 A bug in the quoting of $ inside a ERE pattern ~(E)(pattern) - has been fixed. -08-06-06 A bug when processing `` command substitution with the character - sequence \$' has been fixed. -08-06-02 When defining a type, the typeset -r attribute causes this field - to be required to be specified for each instance of the type and - does not allow a default value. -08-06-02 Several bugs in which compound variables were modified by - subshells have been fixed. -08-05-22 The ceil function has been added to the math functions. -08-05-21 A bug in which a name reference defined in a function and passed - as an argument to another function could cause an incorrect binding. -08-05-21 A bug in freeing compound variables that are local to functions - has been fixed. -08-05-19 The array expansions ${array[sub1..sub2]} and ${!array[sub1..sub2]} - to expand to the value (or subscripts) for array between sub1 and - sub2 inclusive. For associative arrays, the range is based on - location in the POSIX locale. The .. must be explicit and cannot - result from an expansion. -08-05-15 The trap on SIGCLD is no longer triggered by the completion of - the foreground job as with ksh88. -08-05-14 A bug in the implementation of the editing feature added on - 07-09-19 in emacs mode has been fixed. -08-05-12 A bug in processing the test built-in with parenthesis has been - fixed. -08-05-12 The unset built-in now returns non-zero when deleting an array - subscript that is not set. -08-05-08 Changing the value of HISTFILE or HISTSIZE will cause the old - history file to be close and reopened with the new name or size. -08-05-08 When FPATH is changed functions that were found via a path search - will be searched for again. -08-05-08 A parser bug in which reserved words and labels were recognized - inside compound indexed array assignment after a new-line has - been fixed. -08-05-07 A bug in getopts when handling numerical option arguments has - been fixed. -08-05-07 The typeset -S option was added for variables outside type - definitions to provide a storage class similar to C static - inside a function defined with function name. When outside - type definitions and outside a function, the -S option cause - the specified variable so be unset before the assignment and - before the remaining attributes are supplied. -08-05-07 A bug that affected the cursor movement in multiline mode when - a character was deleted from near the beginning of the any - line other than the first. -08-05-01 In multiline edit mode, the refresh operation will now clear - the remaining portion of the last line. -08-05-01 A bug in computing prompt widths for the edit modes for prompts - with multibyte characters has been fixed. -08-05-01 A bug in the multiline edit mode which could cause the current - line to be displayed incorrectly when moving backwards from third - or higher line to the previous line has been fixed. -08-05-01 A bug in which options set in functions declared with the function - name syntax were carried across into functions invoked by these - functions has been fixed. -08-04-30 A bug which could cause a coprocess to hang when the read end - is a builtin command has been fixed. -08-04-30 The emacs and vi editors have been modified to handle window - change commands as soon as they happen rather than waiting for - the next command. -08-04-28 A bug in which ${!x} did not expand to x when x was unset has been - fixed. -08-04-27 A bug in which the assignment x=(typeset -a foo=([0]=abc)) created - x.foo as an associative array has been fixed. -08-04-25 A bug in which $# did not report correctly when there were more - than 32K positional parameters has been fixed. -08-04-04 Choose the name _ as the sub-variable that holds type or instance - specific data used by discipline functions. -08-03-27 A bug in which the terminal group was not given back to the parent - shell when the last part of a pipeline was handled by the parent shell - and the other parts of the pipeline complete has been fixed. - The symptom was that the pipeline became uninterruptable. -08-03-25 A bug in restricted mode introduced in ksh93s that caused scripts - that did not use #! to executed in restricted mode has been fixed. -08-03-25 A bug in which the pipefail option did not work for a pipeline - within a pipeline has been fixed. -08-03-24 A bug in which OPTIND was not set correctly in subshells has - been fixed. -08-03-24 A bug which could cause a memory exception when a compound variable - containing an indexed array with only element 0 defined was expanded. -08-03-20 A bug in which ${!var[sub].*} was treated as an error has been fixed. -08-03-20 Associative array assignments of the form ([name]=value ...) - now allow ; as well as space tab and new line to separate elements. -08-03-18 A buffering problem in which standard error was sometimes - not flushed before sleep has been fixed. -08-03-17 A bug in which a signal sent to $$ while in a subshell would be - sent to the subshell rather than the parent has been fixed. -08-03-17 --default option added to set(1) to handle set +o POSIX semantics. - set --state added as a long name alias for set +o. -08-03-14 A bug in which using monitor mode from within a script could - cause the terminal group to change has been fixed. -08-03-10 The new ${...} command substitution will treat the trailing } - as a reserved word even if it is not at the beginning of a command, - for example, ${ date }. -08-03-10 If the name of the ENV begins with /./ or ././ then the - /etc/ksh.kshrc file will not be executed on systems that support - this interactive initialization file. -08-03-07 A bug in which ksh -i did not run the ENV file has been fixed. -08-03-07 A bug in which ulimit did not always produce the same output as - ulimit -fS has been fixed. -08-03-04 A bug in multiline mode in emacs and vi mode which could cause the - cursor to be on the wrong line when interrupt was hit has been fixed. -08-03-03 The change made in ksh93s+ on 07-06-18 in which braces became - optional for ${a[i]} inside [[...]] was restored in the case - where the argument can be a pattern. -08-03-03 A bug in which creating a name reference to an associative array - instance would fail when the subscript contained characters [ or - ] has been fixed. -08-02-29 The redirection operator >; has been added which for non-special - files, generates the output in a temporary file and writes the - specified file only of the command has completed successfully. -08-02-15 A bug in ${var/pattern/string} for patterns of the form ?(*) and +(*) - has bee fixed. -08-02-07 A bug in which test \( ! -e \) produced an error has been fixed. -08-02-14 The typeset -a option can now optionally be followed by the name - of an enumeration type which allows subscripts to be enumerations. -08-02-14 The enum builtin which creates enumeration types has been added. -08-02-12 The backoff logic when there are no more processes has been fixed. -08-02-07 The -X option has been added to typeset. The -X option creates - a double precision number that gets displayed using the C99 %a - format. It can be used along with -l for long double. -08-01-31 The -T option to typeset has been added for creating typed - variables. Also the -h and -S options have been added to - typeset that are only applicable when defining a type. -08-01-31 The prefix expansion operator @ has been added. ${@name} - expands to the type of name or yields the attributes. -07-11-15 A bug in the macro expander for multibyte characters in which - part of the character contains a file pattern byte has been fixed. -07-10-03 A bug in which : was not allowed as part of an alias name has been - fixed. -07-09-26 A bug in which appending a compound variable to a compound variable - or to an index array didn't work has been fixed. -07-09-19 In both emacs and vi edit mode, the escape sequence \E[A (usually - cursor up, when the cursor is at the end of the line will fetch - the most recent line starting with the current line. -07-09-18 The value of ${!var} was correct when var was a reference to an - array instance. -07-09-18 The value of ${!var[sub]} was not expanding to var[sub] and this - was fixed. It also fixed ${name} where name is a name reference - to var[sub]. -07-09-18 It is now legal to create a name reference without an initialization. - It will be bound to a variable on the first assignment. -07-08-30 A discipline function can be invoked as ${x.foo} and is equivalent - to ${ x.foo;} and can be invoked as x.foo inside ((...)). -07-07-09 A bug in which typeset -a did not list indexed arrays has been - fixed. -07-07-03 The command substitution ${ command;} has been added. It behaves - like $(command) except that command is executed in the current - shell environment. The ${ must be followed by a blank or an - operator. - -08-04-17 --- Release ksh93s+ --- -08-04-17 A bug in which umask was not being restored correctly after a - subshell has been fixed. -08-04-15 A bug in which sending a STOP signal to a job control shell started - from within a shell function caused cause the invoking shell to - terminate has been fixed. -08-04-11 A bug which caused $(exec > /dev/null) to go into an infinite loop - has been fixed. -08-03-27 A bug in which typeset -LZ was being treated as -RZ has been fixed. -08-03-06 A bug with ksh -P on systems that support the the profile shell, - in which it would exit after running a non-builtin has been fixed. -08-01-31 A bug in which command substitution inside ((...)) could cause - syntax errors or lead to core dumps has been fixed. -08-01-17 A bug in which discipline functions could be deleted when invoked - from a subshell has been fixed. -08-01-17 A bug in which a command substitution consisting only of - assignments was treated as a noop has been fixed. -08-01-17 A bug in which discipline functions invoked from withing a - compound assignment could fail has been fixed. -08-01-16 Incomplete arithmetic assignments, for example ((x += )), now - generate an error message. -08-01-16 A bug in which a set discipline defined for a variable before - an array assignment could cause a core dump has been fixed. -08-01-03 A bug in on some systems in which exit status 0 is incorrectly - returned by a process that catches the SIGCONT signal is stopped - and then continued. -07-12-13 A race condition in which a program that has been stopped and then - continued could loose the exit status has been fixed. -07-12-12 Code to check for file system out of space write errors for all - writes has been added. -07-12-11 A bug in the macro expander for multibyte characters in which - part of the character contains a file pattern byte has been fixed. -07-12-06 A bug in the emacs edit mode when multiline was set that output - a backspace before the newline to the screen has been fixed. -07-12-04 A bug in which using <n>TAB after a variable name listing expansion - in the edit modes would cause the $ to disappear has been fixed. -07-11-28 A bug in which setting IFS to readonly could cause a subsequent - command substitution to fail has been fixed. -07-11-27 A work around for a gcc 4.* C99 "feature" that could cause a job - control shell to go into an infinite loop by adding the volatile - attribute to some auto vars in functions that call setjmp(). -07-11-27 A bug in which the shell could read ahead on a pipe causing the - standard input to be incorrectly positioned has been fixed. -07-11-27 A bug in which compound variable UTF-8 multibyte values were not - expanded or traced properly has been fixed. -07-11-21 A bug where an unbalanced '[' in a command argument was not treated - properly has been fixed. -07-11-15 A bug in which compatibility mode (no long option names) getopts(1) - incorrectly set the value of OPTARG for flag options has been fixed. -07-11-15 A bug in which "hash -- name" treated "--" as an invalid name operand - has been fixed. -07-11-15 typeset now handles "-t -- [-r] [--]" for s5r4 hash(1) compatibility. -07-11-15 A bug in which the umask builtin mis-handled symbolic mode operands - has been fixed. -07-11-15 Bugs in which shell arithmetic and the printf builtin mis-handled the - signs of { -NaN -Inf -0.0 } have been fixed. -07-11-15 The full { SIGRTMIN SIGRTMIN+1 ... SIGRTMAX-1 SIGRTMAX } range - of signals, determined at runtime, are now supported. -07-11-15 A bug in which creating an index array with only subscript 0 created - only a simple variable has been fixed. -07-11-14 A bug in which appending to an indexed array using the form - name+=([sub]=value) could cause the array to become an associative - array has been fixed. -07-11-14 A bug in which typeset without arguments could coredump if a - variable is declared as in indexed array and has no elements has - been fixed. -07-11-14 A bug in which creating a local SECONDS variable with typeset in - a function could corrupt memory has been fixed. -07-11-14 A bug which could cause a core dump when a script invoked by name - from a function used compound variables has been fixed. -07-11-05 A bug in which printf %d "'AB" did not diagnose unconverted characters - has been fixed. -07-11-05 printf %g "'A" support added for all floating point formats. -07-11-01 A bug in which typeset -f fun did not display the function definition - when invoked in a subshell has been fixed. -07-10-29 The sleep builtin was fixed so that all floating point constants - are valid operands. -07-10-10 A bug in which the locale was not being restored after - LANG=value command has been fixed. -07-09-20 A bug in which a nameref to a compound variable that was local - to the calling function would not expand correctly when displaying - is value has been fixed. -07-09-19 A bug which cause cause a core dump if .sh.edchar returned - 80 characters or more from a keyboard trap has been fixed. -07-09-14 A bug in which could cause a core dump when more than 8 file - descriptors were in use has been fixed. -07-09-10 A bug in which creating a name reference to an instance of - an array when the array name is itself a reference has been fixed. -07-09-10 The file completion code has been modified so that after an = in - any word, each : will be considered a path delimiter. -07-09-06 A bug in which subprocess cleanup could corrupt the malloc() heap - has been fixed. -07-08-26 A bug in which a name reference to an associative array instance - could cause the subscript to be evaluated as an arithmetic expression - has been fixed. -07-08-22 A bug in which the value of an array instance was of a compound - variable was not expanded correctly has been fixed. -07-08-14 A bug which could cause a core dump when a compound assignment was - made to a compound variable element with a typeset -a attribute - has been fixed. -07-08-08 A bug in which a trap ignored in a subshell caused it to be - ignored by the parent has been fixed. -07-08-07 A bug in which the set command would generated erroneous output - for a variable with the -RZ attribute if the variable name had been - passed to a function has been fixed. -07-08-02 A bug in which read x[1] could core dump has been fixed. -07-08-02 A second bug in which after read x[sub] into an associative array - of an element that hasn't been assigned could lead to a core dump - has been fixed. -07-07-31 A bug in which a pipeline that completed correctly could have - an exit status of 127 when pipefail was enabled has been fixed. -07-07-09 The SHOPT_AUDIT compile option has been added for keyboard logging. -07-06-25 In vi insert mode, ksh no longer emits a backspace character - before the carriage return when the newline is entered. -07-06-25 A bug in which pipefail would cause a command to return 0 - when the pipeline was the last command and the failure happened - on a component other than the last has been fixed. -07-06-25 A bug in the expansion of ${var/pattern/rep} when pattern or rep - contained a left parenthesis in single quotes has been fixed. -07-06-18 The braces for a subscripted variable with ${var[sub]} are now - optional when inside [[...]], ((...)) or as a subscript. -07-05-28 A bug in brace expansion in which single and double quotes did - not treat the comma as a literal character has been fixed. -07-05-24 The -p option of whence now disables -v. -07-05-23 Several bug fixes in compound variables and arrays of arrays - have been made. -07-05-15 A bug in which the %B format of printf was affected by the - locale has been fixed. -07-05-14 A bug in which \ was not removed in the replacement pattern with - ${var/pattern/rep} when it was not followed by \ or a digit has - been fixed. -07-05-10 A bug in which ksh -R file core dumped if no script was specified - has been fixed. it not displays an error message. -07-05-07 Added additional Solaris signals to signal table. -07-04-30 A bug in which a pipeline with command substitution inside a - function could cause a pipeline that invokes this function to - hang when the pipefail option is on has been fixed. -07-04-30 Added -q to whence. -07-04-18 A small memory leak with each redirection of a non-builtin has - been fixed. -07-03-08 A bug in which set +o output command line options has been fixed. -07-03-08 A bug in which an error in read (for example, an invalid variable - name), could leave the terminal in raw mode has been fixed. -07-03-06 A bug in which read could core dump when specified with an array - variable with a subscript that is an arithmetic expression has - been fixed. -07-03-06 Several serious bugs with the restricted shell were reported and - fixed. -07-03-02 If a job is stopped, and subsequently restarted with a CONT - signal and exits normally, ksh93 was incorrectly exiting with - the exit status of the stop signal number. -07-02-26 M-^L added to emacs mode to clear the screen. -07-02-26 A bug in which setting a variable readonly in a subshell would - cause an unset error when the subshell completed has been fixed. -07-02-19 The format with printf uses the new = flag to center the output. -07-02-19 A bug in which ksh93 did not allow multibyte characters in - identifier names has been fixed. -07-02-19 A bug introduced in ksh93 that causes global compound variable - definitions inside functions to exit with "no parent" has been fixed. -07-02-19 A bug in which using compound commands in process redirection - arguments would give syntax errors <(...) and >(...) has been fixed. -07-01-29 A bug which caused the shell to core dump which can occur when a - built-in exits without closing files that it opens has been fixed. -07-01-26 A bug in which ~(E) in patterns containing \ that are not inside () - has been fixed. - -06-12-29 --- Release ksh93s --- -06-12-29 A bug in which the value of IFS could be changed after a command - substitution has been fixed. -06-12-22 /dev/(tcp|udp|sctp)/HOST/SEVRICE now handles IPv6 addresses on - systems that provide getaddrinfo(3). -06-12-19 A -v option was added to read. With this option the value of - the first variable name argument will become the default value - when read from a terminal device. -06-11-20 A bug in which "${foo[@]:1}}" expands a null argument (instead of - no argument), when foo[0] is not empty has been fixed. -06-11-16 The discipline functions have been modified to allow each subscript - to act independently. Currently the discipline function will not - be called when called from a discipline function of the same variable. -06-11-14 A bug which could cause a core dump if a file descriptor for - an internal file was closed from with a subshell has been fixed. -06-10-30 The redirections <# pattern, and <## pattern have been added. - Both seek forward to the beginning of the next line that contains - the pattern. The <## form copies the skipped portion to standard - output. -06-10-26 On systems that support stream control transport, the virtual file - name /dev/sctp/host/port can now be used to establish connections. -06-10-26 The printf modifier # when used with d produces units in thousands - with a single letter suffix added. The modifier # when used with - the i specification provides units of 1024 with a two letter suffix. -06-10-24 The value of $! is now set to the process id of a job put - into the background with the bg command as required by POSIX. -06-10-23 A bug in which the value of $! was affected by a background - job started from a subshell has been fixed. -06-10-23 A bug in ${var:offset:len} in multibyte locales has been fixed. -06-10-15 The remaining math functions from C99 were added for any system - that supports them. -06-10-13 The klockwork.com software detected a few coding errors that - have been fixed. -06-10-12 A bug when skipping over `...` with ${x:=`...`} when x is set - has been fixed. -06-10-11 A bug in process floating constants produced by the %a format - of printf has been fixed. -06-10-06 A bug in which IFS was not being restored correctly in some - cases after a subshell has been fixed. -06-10-06 A bug in which pipefail was not detecting some failures in - pipelines with 3 or more states has been fixed. -06-10-03 A bug in the processing of >(...) with builtins which could - cause the builtin to hang has been fixed. -06-10-03 A bug in the for loop optimizer which causes >(...) process - substitution to be ignored has been fixed. -06-09-17 The -a option was added to typeset for indexed arrays. This - is only needed when using the ([subscript]=value ...) form. -06-09-06 The showme option was added. Each simple command not beginning - with a redirection and not occurring with in the while, until, if, - select condition can be preceded by a semi-colon which will - be ignored when showme is off. When showme is on, any command - preceded by a colon will be traced but not executed. -06-08-16 As a new feature, a leading ~(N) on a pattern has no effect - except when used for file expansion. In this case if not - matches are found, the pattern is replaced by nothing rather - than itself. -06-08-11 A bug in the expansion of ${.sh.match[i]:${#.shmatch[i]}} has - been fixed. -06-08-10 The read builtin options -n and -N have been modified to treat - the size as characters rather than bytes unless storing into a - binary (typeset -B) variable. -06-07-27 When the here document operator << is followed directly by a # - rather than a -, the first line of the here-document determines - how much whitespace is removed for each line. -06-07-26 A bug in the C-shell history (enabled with set -H) in which the - history event !$ was not processed has been fixed. -06-07-21 A bug on some systems in which assigning PATH on a command line - would not take effect has been fixed. -06-07-20 Add ksh93 and rksh93 as allowable names for ksh binaries. -06-07-20 Removed the SHOPT_OO compilation option which was only partially - implemented. -06-07-20 The ability to use egrep, grep, and fgrep expressions within - shell patterns has been documented. -06-07-17 A bug with arithmetic command expressions for locales in which - the comma is a thousands separator has been fixed. -06-07-13 The default HISTSIZE was increased from 128 to 512. -06-07-13 A multibyte problem with locales that use shift codes has been fixed. -06-06-23 A number of bug fixes for command, file, and variable completion - have been mode. -06-06-20 Floating point division by zero now yields the constant Inf or -Inf - and floating functions with invalid arguments yield NaN. -06-06-20 The floating point constants Inf and NaN can be used in arithmetic - expressions. -06-06-20 The functions isinf(), isnan(), tanhl() have been added for - arithmetic expressions. -06-06-13 Internal change to use ordering for variables instead of hashing - to speed up prefix matching. -06-06-13 A window between fork/exec in which a signal could get lost - and cause a program to hang has been eliminated -06-06-13 A bug in edit completion with quoted strings has been fixed. -06-06-07 The restricted options can now be enabled by set as well as on - the command line. Once set, it can not be disabled. -06-06-04 Modified built-in binding so that for systems for which /bin - and /usr/bin are the same, a builtin bound to /bin will get - selected when either /bin or /usr/bin is scanned. -06-06-04 Added literal-next character processing for emacs/gmacs mode. - This change is not compatible with earlier versions of ksh93 - and ksh88 when the stty lnext is control-v. The sequence - escape-control-v will display the shell version. -06-05-31 Modified emacs and vi mode so that entering a TAB after a partial - TAB completion, generates a listing of possible completions. - After the second TAB, a number followed by a TAB will perform - the completion with the corresponding item. -06-05-19 Modified arithmetic so that conversions to strings default to - the maximum number of precision digits. -06-05-16 Bug fixes for multibyte locales. -06-05-10 The =~ operator was added to [[...]] and [[ string ~= ERE ]] - is equivalent to [[ string == ~(E)ERE ]]. -06-04-25 A bug in the vi edit mode which could cause the shell to core dump - when switching from emacs mode. -06-04-17 A bug in which using LANG or LC_ in assignment lists with builtins - did not restore the localed correctly has been fixed. -06-04-04 A bug in which discipline functions could not be added to variables - whose names started with .sh has been fixed. -06-03-28 The -s option to typeset was added to modify -i to indicate short - integers. -06-03-28 A bug in which variables assignment lists before functions - defined with function name were not passed on the functions - invoked by this function has been fixed. -06-03-28 A bug in which name references defined within a function defined - with function name could not be used with compound variables has - been fixed. -06-03-27 A bug in which read <&p (print >&p) would cause the coprocess input - (output) pipe to close before reading from (after writing to) - it has been fixed. -06-02-28 A bug in which stopping a job created with the hist builtin command - would create a job that could not be restarted has been fixed. - -06-01-24 --- Release ksh93r --- -06-01-24 A bug in which running commands with standard output closed would - not work as expected has been fixed. -06-01-23 A bug in which print -u<n> could fail when file descriptor <n> was - open for writing has been fixed. -06-01-19 The ?: arithmetic operator fixed to work when the operation after - the colon was an assignment. -05-12-24 A bug which could lead to a core dump when elements of a compound - variable were array elements, i.e. foo=(bar=(1 2)), has been fixed. -05-12-13 An arithmetic bug in which x+=y+=z was not working has been fixed. -05-12-13 An arithmetic bug in which x||y was returning x when x was non-zero - rather than 1 has been fixed. -05-12-07 The aliases for integer and float have been changed to use attributes - -li and -lE to handle long long and long double types. -05-12-07 The histexpand (-H) option has been added which allows C-shell - style history expansions using the history character !. -05-12-07 The multiline option was added which changes that way the edit - modes handle lines longer than the window width. Instead of - horizontal scrolling, multiple lines on the screen are used. -05-12-05 The whence builtin now returns an absolute pathname when the - command is found in the current directory. -05-11-29 A bug which caused ksh -c '[[ ! ((' to core dump rather than - report a syntax error has been fixed. -05-11-29 A bug when reading fixed length records into typeset -b variables - which caused a zero byte to terminate the value has been fixed. -05-11-22 The ability to seek to an offset within a file has been added - with the new I/O redirection operators, <# and >#. Currently, - these redirection operators must be followed by ((expr)) - but in a future release, it should be able to used to seek forward - to the specified shell pattern. In addition $(n<#) expands to the - current byte offset for file descriptor n. -05-11-22 The .sh.match array variable is now set after each [[ ... ]] - pattern match. Previously it was only set for substring matches. -05-10-17 A bug in which the library path variable could be prefixed - with a directory when a .path file was not encountered in - the directory of the executable has been fixed. -05-09-15 A for/while loop optimizer bug in which $OPTIND was not - correctly expanded has been fixed. -05-09-05 A bug in which a history command that invoked a history - command could go into an infinite loop has been fixed. -05-08-31 In the case that IFS contains to adjacent new-lines so that - new-line is not treated as a space delimiter, only a single - new-line is deleted at the end of a command substitution. -05-08-19 When a tilde substitution expands to the / directory and is - followed by a /, it is replaced by the empty string. -05-08-16 A bug in which n<&m did not synchronize m has been fixed. -05-08-16 A bug in which process substitution ( <() and >() ) was not - working within for and while loops has been fixed. -05-07-24 A bug in which the pattern ~(E)(foo|bar) was treated as a syntax - error has been fixed. -05-07-24 A bug in completion with <n>=, where n was the one of the - previous selection choices has been fixed. -05-07-21 A bug with multibyte input when no edit mode was specified which - caused the input line to shift left/right has been fixed. -05-06-24 A race condition which could cause the exit status to get lost - on some fast systems has been fixed. -05-06-21 A bug in which nested patterns of the form {m,n}(pat) would cause - syntax errors has been fixed. -05-06-21 A bug in the macro expander has been fixed which could cause a - syntax error for an expansion of the form ${x-$(...)} when - x is set and the command substitution contained certain strings. -05-06-08 On systems for which echo does not do System V style \ expansions, - the -e option was added to enable these expansion. -05-06-08 A bug in which ${var op pattern} to not work when inside an - arithmetic expression has been fixed. -05-05-23 An extension to shell patterns that allows matching of nested - groups while skipping over quoted strings has been added. -05-05-18 A bug in which the line number for errors was not correct for - functions loaded from FPATH has been fixed. -05-04-18 A bug in which the exit status $? is not set when a trap triggered - by the [[...]] command is executed has been fixed. -05-04-08 Redirection operators can be directly preceded with {varname} - with no intervening space, where varname is a variable name which - allows the shell to select a file descriptor > 10 and store it - into varname. -05-04-08 SHOPT_CMDLIB_BLTIN=1 now includes <cmdlist.h> generated table. -05-04-07 [[ -o ?option ]] is true if "option" is a supported option. -05-04-05 A bug in handling file completion with spaces in the names - has been fixed. -05-03-25 The SIGWINCH signal is caught by default to keeps the LINES and - COLUMNS variables in sync with the actual window size. -05-03-25 Building ksh with SHOPT_REMOTE=1 causes ksh to set --rc if stdin is - a socket (presumably part of a remote shell invocation.) -05-03-25 Building ksh with SHOPT_SYSRC=1 causes interactive ksh to source - /etc/ksh.kshrc (if it exists) before sourcing the $ENV file. -05-03-25 {first..last[..incr][%fmt]} sequences added to brace expansions - when braceexpand is enabled. -05-03-03 A bug where a SIGCHLD interrupt could cause a fifo open to fail has - been fixed. -05-02-25 A bug in which a builtin command run in the background could - keep a file descriptor open which could cause a foreground - process to hang has been fixed. -05-02-24 A bug where builtin library commands (e.g., date and TZ) failed to - detect environment variable changes has been fixed. -05-02-22 The read builtin and word splitting are now consistent with respect - to IFS -- both treat IFS as field delimiters. -05-02-22 The read builtin no longer strips off trailing delimiters that - are not space characters when there are fewer variables than fields. -05-02-17 A builtin bug on systems where dlsym(libcmd) returns link-time - bindings has been fixed. -05-02-12 A bug in which the lib_init() function for .paths BUILTIN_LIB - libraries was not called has been fixed. -05-02-06 A bug on some systems in which moving the write end of a co-process - to a numbered file descriptor could cause it to close has been fixed. -05-02-06 A bug in the vi-edit mode in which the character under the cursor - was not deleted in some cases with the d% directive has been fixed. -05-02-06 A bug where external builtin stdout/stderr redirection corrupted - stdout has been fixed. -05-02-04 A bug where times formatting assumed CLK_TCK==60 has been fixed. - -05-01-11 --- Release ksh93q --- -05-01-11 A bug in the integral divide by zero check has been fixed. -05-01-11 The -l option has been added to read /etc/profile and - $HOME/.profile, if they exist, before the first command. -05-01-11 An argument parsing bug that caused `kill -s x -- n' to fail has - been fixed. -05-01-11 The .paths file, introduced in ksh93m, which can appear in - any directory in PATH, now allows a line of the form 'BUILTIN_LIB=.' - When a command is searched for this directory, and the full path - matches the path of the built-in version of the command (listed - by the 'builtin' command) then the built-in version of the command - is used. When ksh is built with SHOPT_CMDLIB_DIR=1 then all libcmd - functions become builtins with the '/opt/ast/bin/' directory prefix. -05-01-10 A bug in which a nameref to a compound name caused a core dump has - been fixed. -05-01-09 A bug in which some SIGCHLD interrupts (from child processes exiting) - caused a fatal print/echo error diagnostic has been fixed. -04-12-24 A bug in which some SIGCHLD interrupts (from child processes exiting) - corrupted the internal process/job list, sometimes causing the shell - to hang, has been fixed. -04-12-01 A bug in which typeset -Fn truncated less than n digits for large - numbers has been fixed. -04-11-25 A bug in which standard error could be closed after a redirection - to /dev/stderr has been fixed. -04-11-17 A bug in which an expansion of the form ${array[@]:3} could expand - to ${array[0]} when ${array[3]} was not set has been fixed. -04-10-22 The -E or -orc command line option reads ${ENV-$HOME/.kshrc} file. -04-10-22 `-o foo' equivalent to `+o nofoo', `-o nobar' equivalent to `+o bar'. - `--foo' equivalent to `-o foo', `--nofoo' equivalent to `+o foo' -04-10-05 The .paths file, introduced in ksh93m, which can appear in - any directory in PATH, now allows a line of the form - 'BUILTIN_LIB=libname'. When a command is searched for this directory, - the shared library named by libname will first be searched for a - built-in version of the command. -04-09-03 <<< here documents now handle quotes in the word token correctly. -04-08-08 The maximum size for read -n and and read -N was increased from - 4095 to 32M. -04-08-04 printf %q was modified so that if an no operand was supplied, no - no output would be generated rather than a quoted empty string. -04-08-01 The -n and -N options of the read builtin has been modified - when reading variables with the binary attribute so that the - data is stored directly rather than through assignment. -04-08-01 The shcomp command has been modified to process alias commands - under some conditions. -04-07-31 The .sh.match variable added in ksh93l, now works like other - indexed arrays. -04-07-08 A loop optimizer bug which occurs when typeset is used in - a for or while loop inside a function has been fixed. -04-06-24 The number of subexpressions in a pattern was increased to 64 - from the current number of 20. -04-06-17 The -t option to read was modified to allow seconds to be - specified as any arithmetic expression rather than just - an integral number of seconds, for example even -t 'sin(.5)' - is now valid. -04-06-16 Two small memory leak problems were fixed. -04-06-15 A bug in ${var/pattern/"string"} which occurred when string - contained pattern matching characters has been fixed. -04-05-08 printf $'%d\n' produced an erroneous error message and has - been fixed. -04-05-24 A bug in which an associative array without any elements could - cause a core dump when a script with an associative array with - the same name was declared in a script invoked by name has - been fixed. -04-05-11 A bug in which an exec statement could close the script that - is being processed in a script that is run by name causing - a failure has been fixed. -04-04-28 If the first character of assignment to an integer variable was 0, - the variable had been treated as unsigned. This behavior was - undocumented and has been removed. -04-04-05 A bug in which the positioning of standard input could be incorrect - after reading from standard input from a subshell has been fixed. -04-03-30 A bug in the for loop optimizer which in rare cases could cause - memory corruption has been fixed. -04-03-29 The preset alias source='command .' has been added. -04-03-29 A bug introduced in ksh93p on some systems in which invoked by - name with #! on the first line would not get the signals handling - initialized correctly has been fixed. -04-03-29 A bug introduced in ksh93p in which a HUP signal received by - a shell that is a session group leader was not passed down to - its children has been fixed. - -04-02-28 --- Release ksh93p --- -04-02-28 The ability to apply an append discipline to any variable has - been added. -04-02-14 A bug in which the exportall option (set -a) would cause incorrect - results for arrays has been fixed. -04-02-02 A bug in which an exported array would pass more than - the first element to a script invoked by name has been fixed. -04-02-02 A bug on some systems in which name=value pairs preceding a script - invoked by name was not getting passed to the script has been fixed. -04-01-20 A bug in which an unset discipline function could cause a core - dump on some systems has been fixed. -04-01-12 A bug in which a continue or break called outside a loop from - inside a function defined with name() syntax could affect - the invoking function has been fixed. -04-01-08 If a command name begins with ~, only filename completion will be - attempted rather than pathname completion using the builtin editors. -04-01-08 A bug in the vi edit mode in which the wrong repeat count on - multiple word replacements with the . directive has been fixed. -04-01-06 Backspace characters are now handled correctly in prompt strings. -04-01-06 The getopts builtin has been modified to accept numerical - arguments of size long long on systems that support this. -04-01-06 A bug in which unsetting all elements of an associative array - would cause it to be treated as an indexed array has been fixed. -03-12-15 A bug in which a quoted string ending with an unescaped $ would - delete the ending $ in certain cases has been fixed. -03-12-05 A bug in which the shell could hang when set -x tracing a command - when an invalid multibyte character is encountered has been fixed. -03-12-05 On some systems, if the KEYBD trap is set, then commands that use - the meta key were not processed until return was hit. This - has been fixed. -03-12-05 A problem which occurred when the login shell was not a group - leader that could cause it to fail has been fixed. -03-12-05 A problem in which a shell could core dump after receiving a signal - that should cause it to terminate while it was in the process - of acquiring more space has been fixed. -03-12-05 If ENV is not specified, the shell will default to $HOME/.kshrc - for interactive shells. -03-11-21 A bug introduced in ksh93o in which the DEBUG trap could get - disabled after it triggered has been fixed. -03-11-04 A bug in which using arithmetic prefix operators ++ or -- on a - non-lvalue could cause a core dump has been fixed. -03-11-04 A bug in which leading zeros were stripped from variable - expansions within arithmetic computation to avoid being treated - as octal constants when they should not have, has been fixed. -03-10-08 A bug introduced in ksh93o in which a large here document inside - a function definition could get corrupted has been fixed. -03-09-22 A bug in which the .get discipline function was not being - called when a string variable was implicitly referenced from - within a numerical expression has been fixed. -03-09-22 A bug in which a script without a leading #! could get executed - by /bin/sh rather than the current shell on some systems has - been fixed. -03-09-12 To improve conformance with ksh88, leading zeros will be ignored - when getting the numerical value of a string variable so that - they will not be treated as octal constants. -03-09-03 The builtin kill command now processes obsolete invocations - such as kill -1 -pid. -03-09-02 The restriction on modifying FPATH in a restricted shell (sh -r) - has been documented. -03-09-02 The restricted shell (sh -r) has been modified to disallow - executing command -p. -03-08-07 A bug in which the KEYBD trap was not being invoked when - characters with the 8th bit set has been fixed. -03-08-02 A parser bug introduced in ksh93o which caused the character - after () in a Posix function definition to be skipped - when reading from standard input has been fixed. -03-08-01 A bug in which "${foo#pattern}(x)" treated (x) as if it were - part of the pattern has been fixed. -03-08-01 The command -x option has been modified so that any trailing - arguments that do expand to a single word will be included - on each invocation, so that commands like command -x mv * dir - work as expected. - -03-07-20 --- Release ksh93o+ --- -03-07-20 A bug in which could cause memory corruption when a posix - function invoked another one has been fixed. -03-07-15 A bug in which a file descriptor>2 could be closed before - executing a script has been fixed. -03-07-15 A parsing error for <() and >() process substitutions inside - command substitution has been fixed. -03-07-15 A parsing error for patterns of the form {...}(...) when - used inside ${...} has been fixed. -03-07-15 An error in which expanding an indexed array inside a compound - variable could cause a core dump has been fixed. -03-07-15 A bug in which on rare occasions a job completion interrupt - could cause to core dump has been fixed. -03-06-26 A bug in which process substitution embedded within command - substitution would generate a syntax error has been fixed. -03-96-23 A bug in which ${@:offset:len} could core dump when there - were no arguments has been fixed. -03-96-23 A bug in which ${X[@]:offset:len} could core dump when X - was unset has been fixed. -03-06-22 The -x option was added to the command builtin. If this - option is on, and the number of arguments would exceed ARG_MAX, - the command will be invoked multiple times with a subset of - the arguments. For example, with alias grep='command -x grep, - any number of arguments can be specified. -03-06-14 A bug in which could cause a core dump on some systems with - vi and emacs editors with the MULTIBYTE option has been fixed. -03-06-06 A bug in which the shell could core dump when a script was - run from its directory, and the script name a symlink to a file - beginning with .., has been fixed. -03-06-05 A bug in which the shell could core dump when a child process - that it is unaware of terminates while it is calling malloc() - has been fixed. -03-06-02 An option named globstar (set -G) has been added. When enabled, - during pathname expansion, any component that consists only of ** is - matches all files and any number of directory levels. -03-05-30 A bug in which the PATH search could give incorrect results when - run from directory foo and PATH contained .:foo:xxx has been fixed. -03-05-29 Some changes were made to the code that displays the prompt in edit - mode to better handle escape sequences in the prompt. -03-05-27 I added = to the list of characters that mark the beginning of - a word for edit completion so that filenames in assignments - can be completed. -03-05-20 A bug in which read -N could hang on some systems when reading - from a terminal or a pipe has been fixed. -03-05-19 A bug in which the output of uname from a command substitution - would go to the standard output of the invoking command when - uname was invoked with a non-standard option has been fixed. -03-05-19 A job control bug which would cause the shell to exit because - it hadn't take back the terminal has been fixed. The bug - could occur when running a function that contained a pipeline - whose last element was a function. -03-05-19 A job control timing bug introduced in ksh93o on some systems - which could cause a pipeline to hang if the first component - completed quickly has been fixed. -03-05-13 The read builtin has been modified so that the builtin editors - will not overwrite output from a previous incomplete line. -03-05-13 A bug in which the name of an identifier could have the string - .sh. prefixed to it after expanding a variable whose name begins - with .sh. has been fixed. -03-05-13 A bug in the expansion of $var for compound variables in which - some elements would not be output when the name was a prefix - of another name in the compound variable has been fixed. -03-05-08 The last item in the ksh93o release on 03-01-02 has been - altered slightly to preserve the leading 0's when the - preceding character is a digit. Thus, with typeset -LZ3 x=10, - $(( 1$x)) will be 1010 whereas $(( $x) will be 10. -03-04-25 A bug in which if x is a name reference, then nameref y=x.foo - did not follow x has been fixed. - -03-03-18 --- Release ksh93o --- -03-03-18 A -N unary operator was added to test and [[...]] which returns - true if the file exists and the file has been modified since it - was last read. -03-03-18 The TIMEFORMAT variable was added to control the format for - the time compound command. The formatting description is - described in the man page. -03-03-06 A -N n option was added to read which causes exactly n bytes - to be read unlike -n n which causes at most n bytes to be read. -03-03-03 Three new shell variables were added. The variable .sh.file - stores the full pathname of the file that the current command - was found in. The variable .sh.fun names the current function - that is running. The variable .sh.subshell contains the depth - of the current subshell or command substitution. -03-03-03 When the DEBUG trap is executed, the current command line after - expansions is placed in the variable .sh.command. The trap - is also now triggered before each iteration of a for, select, - and case command and before each assignment and redirection. -03-02-28 Function definitions are no longer stored in the history file so - that set -o nolog no longer has any meaning. -03-02-28 All function definitions can be displayed with typeset -f not - just those stored in the history file. In addition, typeset +f - displays the function name followed by a comment containing the - line number and the path name for the file that defined this function. -03-02-28 A bug in which the value of $LINENO was not correct when executing - command contained inside mult-line command substitutions has been - fixed. -03-02-19 Since some existing ksh88 scripts use the undocumented and - unintended ability to insert a : in front of the % and # parameter - expansion operators, ksh93 was modified to accept :% as equivalent - to % and :# as equivalent to # with ${name op word}. -03-02-14 A bug which could cause a core dump when reading from standard - error when standard error was a pty has been fixed. -03-02-14 The shell arithmetic was modified to use long double on systems - that provide this data type. -03-02-09 A bug in which a function located in the first directory in FPATH - would not be found when the last component of PATH was . and the - current directory was one of the directories in PATH has been fixed. -03-02-07 The trap and kill builtin commands now accept a leading SIG prefix - on the signal names as documented. -03-02-05 A bug in the expansion of ${var/$pattern}, when pattern contained - \[ has been fixed. -03-02-05 A bug in which .sh.match[n], n>0, was not being set for substring - matches with % and %% has been fixed. -03-01-15 A bug in which getopts did not work for numerical arguments specified - as n#var in the getopts string has been fixed. -03-01-09 A bug in which using ${.sh.match} multiple times could lead to - a memory exception has been fixed. -03-01-06 A bug in the expansion of ${var/pattern/$string} in the case that - $string contains \digit has been fixed. -03-01-02 A -P option was added for systems such as Solaris 8 that support - profile shell. -03-01-02 For backward compatibility with ksh88, arithmetic expansion - with ((...)) and let has been modified so that if x is a zero-filled - variable, $x will not be treated as an octal constant. - -02-12-05 --- Release ksh93n+ --- -02-11-30 A bug that can show up in evaluating arithmetic statements that - are in an autoloaded function when the function is autoload from - another function has been fixed. -02-11-30 An optimization bug in which an expansion of the form ${!name.@}, - which occurred inside a for or a while loop, when name is a name - reference, has been fixed. -02-11-18 A bug in which modifying array variables in a subshell could leave - side effects in the parent shell environment has been fixed. -02-11-18 A memory leak when unsetting an associative array has been fixed. -02-11-14 The code to display compound objects was rewritten to make - it easier for runtime extensions to reuse this code. -02-11-14 A change was made to allow runtime builtins to be notified when - a signal is received so that cleanup can be performed. -02-10-31 User applications can now trap the ALRM signal. Previously, - the ALRM signal was used internally and could not be used - by applications. -02-10-31 A bug in which signals received while reading from a coprocess - for which traps were set was not handled correctly has been fixed. -02-10-31 A bug in which a file opened with exec inside a subshell could - be closed before the subshell completed has been fixed. -02-10-21 A bug in which setting PATH or FPATH inside a function might not - take effect has been fixed. -02-10-21 A bug which could cause a core dump when a local SECONDS variable - is defined in a function has been fixed. -02-10-15 A bug in which the associate array name operator ${!array[@]} - could return the same name multiple times has been fixed. -02-10-15 A bug in which the zero'th element of an associative array was - not getting set when an assignment was made without a subscript - specified has been fixed. - -02-09-30 --- Release ksh93n --- -02-09-30 The maximum indexed array size was increased to 16Megs. -02-09-30 A bug which could cause a core dump when changing attributes - of associative array has been fixed. -02-09-30 A bug in which exporting an array variable would not export the - 0-th element has been fixed. -02-09-30 A bug in which an array assignment of the form a=($a ...) would unset - 'a' before the right hand side was evaluated has been fixed. -02-09-27 A bug in which the error message for ${var?message} when var was - null or unset did not contain the variable name var has been fixed. -02-09-27 A bug in which closing file descriptors 0 through 2 could - cause a subsequent here document to fail has been fixed. -02-09-14 A bug in whence which occurs when the specified name contained - a / has been fixed. -02-09-14 A bug in the parser for strings of the form name$((expr))=value - has been fixed. -02-09-14 A for loop optimization bug in which the number of elements in - an array was treated as an invariant has been fixed. -02-09-09 A bug in which redirection or closing of a file descriptor between - 3 and 9 could cause a subsequent here document to fail has been - fixed. -02-09-09 A bug in which a background job was not removed from the job list - when a subshell completed has been fixed, for example (prog&). -02-09-03 A bug in which an assignment of the form name=(integer x=3) - could be interpretted as an array assignment rather than a - compound variable assignment has been fixed. -02-08-19 A command completion bug which occurred on file systems that - are case insensitive has been fixed. -02-08-19 A bug which could lead to an exception on some systems (for - example FREEBSD) which occurred when setting PATH has been fixed. -02-08-11 A bug in arithmetic rounding in which a value input as a decimal - string would output as a rounded version of the string has - been fixed. -02-08-11 A bug in which the last character could be deleted from shell - traces and from whence when called from a multibyte locale - has been fixed. -02-08-01 A bug which could cause a core dump to occur when a shell script - is executed while a coprocess is running that has closed the - output pipe has been fixed. -02-08-01 A bug in which command completion in multibyte mode could - corrupt memory for long command lines has been fixed. - -02-06-17 --- Release ksh93n- --- -02-06-17 A bug in which user defined macros could cause a core dump in - with MULTIBYTE mode has been fixed. -02-06-17 A bug in which printf format specifiers of the form %2$s were causing - a core dump has been fixed. -02-06-17 A bug in which setting stty to noecho mode did not prevent the - echoing of characters by ksh when emacs or viraw mode - was enabled has been fixed. -02-06-17 A bug in which background job completion could cause the sleep - builtin to terminate prematurely has been fixed. -02-06-17 A bug in which the shell could core dump if getopts was called - when the OPTIND variable contained a negative value has been fixed. -02-06-10 The edit mode prompt has been modified to handle escape sequences. -02-06-10 A bug which occurred for interactive shells in which the builtin - cat command was used in command substitution on a file whose - size was larger than PIPE_BUF has been fixed. -02-06-10 A bug in which the trap on ERR was not being processed when - set inside a function has been fixed. -02-06-07 A bug in which function definitions could cause the history count - to be decremented by one (and even become negative) has been fixed. -02-06-05 A bug in read in which share mode could be enabled has been fixed. -02-05-28 A bug which could occur when the last command of a script was - a case statement and the action selected ended in ;& instead of ;; - has been fixed. -02-05-23 A bug with unary + introduced in ksh93k has been fixed. -02-05-07 A bug in substitutions of the form ${var/pattern/string} in which - a backslash was inserted in the replacement string when it contained - a special pattern character has been fixed. -02-05-01 A bug in the emacs edit mode which occurred in versions compiled - for multibyte character sets which occurred when a repeated search - was requested after a long line had been returned for the previous - search has been fixed. -02-04-02 vi and emacs edit modes were modified so that tab completion is - disabled when invoked from the read built-in. - -02-03-26 --- Release ksh93m+ --- -02-03-26 A bug in which \ was not handled correctly when used in file - expansion has been fixed. -02-02-18 A bug in which lines beginning with a # were deleted from here - documents when the here-document delimiter was followed by - a comment has been fixed. -02-12-06 An optimization bug in which ${!x[@]) was treated as invariant in - a for loop has been fixed. -02-02-06 A bug in which the ERR trap is not cleared for a script invoked - by name from within a function has been fixed. -02-01-08 A bug in which a shell script executed from within a subshell - could cause this script to have an invalid pointer leading - to a memory fault has been fixed. -02-01-07 Added here documents of the form <<< word (as per zsh) which - is equivalent to << delim\nword\ndelim. -02-01-07 A bug in which the first word of a compound assignment, - x=(word ...), was treated as a reserved word has been fixed. -02-01-07 A bug in the handling of \ when noglob was enabled and a - substitution of the form ${word op pattern} occurred in the - same word has been fixed. -02-01-07 A compilation option, CMDLIB_BLTIN in the file OPTION, has - been added. When this options is set, all commands implemented - in libcmd become shell builtin commands by default. -02-01-07 A bug in which builtin foo, where foo is already a builtin - would result in the builtin foo getting removed has been fixed. -02-01-07 A bug which the shell executed a command found in the current - directory when PATH have no valid directories has been fixed. -01-11-28 The value of $? was not being set when called with exit. -01-11-28 If the last command was of the form (...) and a trap on EXIT or - ERR was set, and the command inside () modified the trap, then - the original trap wasn't executed. -01-11-26 The value for 0 is now preceded by the base number when - the base was not 10. -01-11-26 The default has compilation mode has been changes so that - viraw mode will always be on. - -01-10-31 --- Release ksh93m --- -01-10-31 A for loop optimizer bug for subshells contained withing for - loops has been fixed. -01-10-16 typeset without arguments no longer outputs variable names - that do not have any attributes that are set. -01-10-16 A bug introduced in ksh93l in which assignments specified with - the exec built-in were not being expanded properly has been - fixed. -01-10-11 An optimization bug in which ${!x) was treated as invariant in - a for loop has been fixed. -01-10-11 Unsigned integer variables in bases other than 10 are printed now - expand in that base with the base prefix. -01-10-10 A number of typos in the self generating man pages for shell - built-ins have been fixed. -01-10-04 The self generated man pages for hist and fc were not working - correctly and have been fixed. -01-10-03 Yet another optimizer bug in which shell patterns were - treated as invariants has been fixed. -01-09-27 Two bugs relating to multibyte history searches and to find - have been fixed. -01-09-27 A bug introduced in ksh93k in which the PATH searching was - not restored after running a command with an assignment list - has been fixed. -01-09-26 A bug in which a zero filled field was treated as octal when - converted to integer has been fixed. -01-09-26 Yet another bug in the optimization of for loops related to - recursive functions with break or continue statements has been fixed. -01-09-25 The exponentiation operator ** was added to the shell arithmetic - evaluation. It has higher precedence than * and is left - associative. -01-09-25 The code was modified to use the ast multibyte macros - and functions for handing multibyte locales. -01-09-25 The expansion ${parameter:offset:length} now handles negative - offsets which cause offsets to be measured from the end. -01-09-25 Some spelling errors in the documentation were corrected. -01-09-24 The /dev/tcp/host/port and /dev/udp/host/port now allow - the ports to be specified by service name. -01-09-24 The change staring with ksh93g in which the the appropriate - library path variable is prepended with a corresponding library - directory has been modified. With the new method, only the - library path defined in the file named .paths in the directory - where the executable is found will be modified. See the - man page for more details. -01-09-23 The .fpath file (see ksh93h) is no longer looked for in each - directory on the path to locate function directories. The - file named .paths is used instead. -01-09-23 A bug in which IFS was not being restored after being changed in - a subshell has been fixed. -01-09-16 With the vi and emacs edit modes, after a list of command - or functions is generated with = or M-= respectively, - any element from the list can be pasted on the command line - by preceding the = or M-= with a numeric parameter specifying - the position on the list. -01-09-16 A bug in ksh93l caused command completion not to find aliases - and functions. Command listing from the edit mode was presented - in reverse order. This has been fixed. -01-09-13 Another bug in the optimization of for loops related to subshells - when traps were set has been fixed. -01-09-07 A change in ksh93l caused brace expansion to stop working - and this has been fixed. -01-09-04 A bug introduced in ksh93k in which an arithmetic statement - within a function that used name references did not follow the - reference has been fixed. -01-09-04 A bug introduced in ksh93l in which export -p did not prefix - each export with the word export has been fixed. -01-08-29 A bug in multibyte input which occurred when a partial multibyte - character was received has been fixed. -01-08-29 A bug introduced in ksh93l which could cause a core dump - when an assignment list containing PATH is specified inside - command substitution has been fixed. -01-08-09 Another bug in the optimization of for loops in ksh93l caused - errors in recursive functions using local variables that - contained for loops has been fixed. -01-07-27 A bug in which IFS would be unset after a command substitution - inside a here document has been fixed. -01-07-26 To conform to the POSIX standard, if you invoked ksh name, - and name does not contain a /, it will first try to run - one in the current directory whether it is executable or not - before doing a path search for an executable script. Earlier - versions first checked for an executable script using the - PATH variable. -01-07-23 A bug in which unset -f invoked in a subshell could unset a - function defined in the parent has been fixed. -01-07-16 A bug in the optimization of for loops in ksh93l caused - name references to be treated as invariants has been fixed. -01-07-09 A bug in which a discipline function applied to a local variable - could cause a shell exception has been fixed. Discipline - functions can only be specified for global variables. - -01-06-18 --- Release ksh93l --- -01-06-18 A bug in assigning integers larger than can be represented as - long integers to floating point variables has been fixed. -01-06-18 A bug in the handling of unsigned integers (typeset -ui) has - been fixed. -01-06-04 The evaluation of the PS1 prompt no longer effects the value - of the $? variable. -01-06-01 A small memory leak from subshells has been fixed. -01-05-22 A bug in which attributes for variables that did not have - values would be lost after a subshell has been fixed. -01-05-22 The %R format has been added to convert a shell pattern into - an extended regular expression. -01-05-22 The escape sequences \e, \cX, \C[.collating-element.], and - \x{hex} have been added to ASCII-C strings and to printf format - strings. -01-05-20 Patterns of the form {n}(pattern) and {m,n}(pattern) are now - recognized. The first form matches exactly n of pattern whereas, - the second form matches from m to n instances of pattern. -01-05-20 The shell allows *-(pattern), +-(pattern), ?-(pattern), - {m,n}-(pattern}, and @-(pattern) to cause the minimal - match of pattern to be selected whenever possible rather - than the maximal (greedy) match. -01-05-20 The character class [:word:] has been added to patterns. - The word class is the union of [:alnum:] and the character _. -01-05-20 Inside (...) pattern groups, the \ character is now treated - specially even when in an enclosing character class. The - sequences, \w, \d, \s are equivalent to the character classes - word, digit, and space respectively. The sequences \W, \D, - and \S are their complement sets. -01-05-20 The shell now recognizes pattern groups of the form - ~(options:pattern) where options or :pattern can be omitted. - Options use the letters + and - to enable and disable options - respectively. The option letters g (greedy), i (ignore case) - are used to cause maximal matching and to cause case - insensitive matching respectively. If :pattern is also - specified, these options are only in effect while this - pattern is being processed. Otherwise, these options remain - in effect until the end of the pattern group that they are contained - in or until another ~(...) is encountered. These pattern groups - are not counted with respect to group numbering. -01-05-14 When edit completion, expansion, or listing occurs in the - middle of a quoted string, the leading quote is ignored when - performing the completion, expansion, or listing. -01-05-14 A small memory leak from subshells has been fixed. -01-05-10 A bug in which open files were not restored after a subshell - that had used exec to replace a file has been fixed. -01-05-10 Redirection to a null file name now generates an error message. -01-05-09 The shell now rejects some invalid parameter substitutions that - were previously processed in undefined ways. -01-05-09 A bug in which the output of select was not flushed before the - read when input did not come from the terminal has been fixed. -01-05-08 A bug in which job ids would not be freed for interactive shells - when subshells ran built-ins in the background has been fixed. -01-05-08 The FPATH variable now requires an explicit . to cause the - current directory to be treated as a function directory. -01-05-08 A bug in read -n when echo mode was disabled has been fixed. -01-05-07 A bug in which function definitions could be listed as part - of the history has been fixed. -01-04-30 This release uses a new and often much faster pattern matcher than - earlier releases. -01-04-30 An optimizer now eliminates invariant parameter expansions from - for while and until loops. -01-04-30 The variable .sh.match is set after each pattern match (# % or /) - in a variable substitution. The variable .sh.match is an - indexed array with element 0 being the complete match. - The array is only valid until the next subsequent pattern - match or until the value of the variable changes which ever - comes first. -01-04-30 A self generating man page has been added to shcomp. Also, - shcomp now stops compiling when it finds an exit or exec - command and copies the remainder so that it can be used - for standard input. -01-04-30 The shcomp command was modified so that it can work in an - EBCIDIC environment and that binary scripts are portable - across environments. -01-04-30 A bug in the handling of a trailing : in PATH has been fixed. -01-04-30 A bug in which the builtin version of a command would get invoked - even though the full pathname for the command was specified - has been fixed. -01-04-30 A bug in which read would loose the last character when - reading the last line of a file that did not contain a new-line - character has been fixed. -01-04-23 A bug on some systems in which in vi mode the end of file - character and end of line character could be swapped has - been fixed. -01-04-23 A bug on some systems in which invoking a shell script that - did not have execute permission could set the exit value to - 127 rather than 126 has been fixed. -01-04-20 A bug in which read -n from a pipe would block if fewer than - n characters was received has been fixed. -01-04-09 A bug in which invalid patterns, for example, ) by itself, - was not treated as a string has been fixed so that if i=')', - then [[ $i == $i ]] is true. -01-04-09 The shell arithmetic now interprets C character constants. -01-04-09 A bug in which a non-zero return from a function defined - with the function reserved word did not trigger the ERR - trap or exit with set -e has been fixed. -01-04-02 A bug on some systems, in which characters above 127 were - not displayed correctly in vi or emacs edit mode has been fixed. -01-04-02 A bug on some systems, introduced in the 'k' point release, in - which the erase character in viraw mode was moving the cursor - to the left without erasing the character has been fixed. -01-04-02 On some systems the wcwith() function was returning a wrong - value for characters and caused characters to be displayed - incorrectly from the shell edit modes. A work around for - this problem has been added. -01-03-26 A bug in which valid scripts could produce syntax errors - when run with locales that considered characters such as "'" - to be space characters has been fixed. -01-03-20 A bug in which an syntax error in an arithmetic expression - entered interactively could cause the shell to go into - an infinite loop outputting the error message has been fixed. -01-03-10 ksh93 accepts -l as a synonym for -L in test on systems for - which /bin/test -l tests for symbolic links. -01-03-10 A bug in parsing scripts in which { and } are used in place of - in and esac in case statements embedded in compound commands - has been fixed. Use of { and } for in and esac is obsolete. -01-03-06 A bug in which an argument of the form foo=bar was not - being passed correctly to a traced function whose name - was foo has been fixed. -01-03-02 Using $(trap -p name) did not print the name of the current - trap setting for trap name. -01-02-26 Exported floating point variables gave incorrect results - when passing them to ksh88. This has been fixed. -01-02-25 A race condition in which a coprocess which completed too quickly - would not allow subsequent coprocesses to start has been fixed. -01-02-25 The 'g' format specifier is now handled by printf. It had - inadvertently been omitted. -01-02-20 The + was not being displayed during an execution trace - with the += assignment operator. -01-02-19 The error message which occurs when the interpreter name - defined on the #! line does not exist is more informative. -01-02-19 A bug in which $0 would not be set correctly when a - script with #! was invoked by full pathname from the - directory of the script has been fixed. -01-02-19 A shell script did not always pick up tty mode changes - made by external commands such as stty which could - effect the behavior of read. -01-02-19 The -u, -g, and -k unary tests did not give the correct - results when used with negation and this has been fixed. - -01-02-05 --- Release ksh93k+ --- -01-02-05 The sequence \<newline> inside $'...' was not incrementing - the line count and this has been fixed. -01-02-05 Modified expansion of "${@-}" so that if no arguments are set - it results in null string rather than nothing. -01-02-02 memory leak problem with local variables in functions fixed. -01-01-25 allow arithmetic expressions with float%int and treat them - as ((int)float)%int rather than as an error. -01-01-19 read -n1 was not working and has been fixed. -01-01-17 ksh now handles the case in which a here document in command - substitution $() is terminated by the trailing ). Previously, - a new-line was needed at the end of the delimiter word. -01-01-02 A bug in which a KEYBD trap would cause a multi-line token - to be processed incorrectly has been fixed. -00-12-10 Arithmetic integer constants can now have L and U suffices. -00-12-10 A bug in the processing of arithmetic expressions with compound - variables when the -n option is on has been fixed. -00-12-08 A bug in M-f and M-b from emacs mode has been fixed. This - bug only occurs when ksh93 is compiled without MULTIBYTE enabled. -00-11-29 A bug in which jobs -p would yield 0 for background - jobs run in a script has been fixed. -00-11-21 A bug in integer arrays in which the number of elements is - incorrect when the ++ operator is applied to a non-existing - element has been fixed. For example, integer x; ((x[3]++)). -00-11-20 A timing bug in which the shell could reset the terminal - group to the wrong value in the case that the a new process - changes the terminal group during startup has been fixed. - -00-10-27 --- Release ksh93k --- -00-10-27 Using tab for completion now works only when applied - after a non-blank character at the end of the current line. - In other case a tab is inserted. -00-10-27 A bug in the emacs edit mode for ^X^E has been fixed. - The ^X^E sequence is supposed to invoke the full editor - on the current command. -00-10-18 A bug in which expansions of the form ${var//pattern/string} - did not work correctly when pattern was '/' or "/" has - been fixed. -00-10-18 The output format for indexed arrays in compound variables - has been modified so that it can be used as input. -00-10-18 Assignments with name references (typeset -n) will now - implicitly unreference an existing name reference. -00-10-17 A bug the += append operator when a single array element - is appended to a variable that is not an array has been fixed. -00-10-16 A bug in which the SIGCONT signal was being sent to - each process will kill -0 or kill -n 0 has been fixed. -00-10-12 The arithmetic evaluation portion has been rewritten to - perform a number of optimizations. -00-10-10 A bug in which name prefix matching ${!name.*} was not - checking name to see if it was a name reference has been fixed. -00-09-26 A bug in the multibyte version in which the width of for - non-printing characters was not correct has been fixed. -00-09-12 Made changes to get multibyte editing work on UWIN for windows -00-09-12 A bug in which multibyte characters would be displayed incorrectly - has been fixed. -00-08-08 Removed build dependency on iswprint() and iswalph(). -00-07-20 In some cases the read builtin would read more than a single - line from a pipe on standard input and therefore leave the seek - position in the wrong location. -00-07-05 If the directory / is on the path, a / will not be inserted - between the directory and the file name during path searching - to avoid searching // for systems that treat this specially. -00-06-26 A bug in which on rare occasions wait could return before all - jobs have completed has been fixed. -00-06-21 A bug in which backspace did not work correctly during the - R replace directive in vi-mode has been fixed. -00-06-12 Added variable name completion/expansion/listing to the set of - completions. Variable name completions begin with $ or "$ followed - by a letter. -00-05-09 --- Release ksh93j --- -00-05-09 Modified command substitution to avoid using /tmp files when - run on read-only file systems. -00-04-17 Modified printf to handle '%..Xc' and '%..Xs' options where X - is not an alpha character. Previous versions core dumped with this. -00-04-10 Changes to multibyte editing code were made to use standard - ISO C functions rather than methods devised before the standard. -00-04-09 Add %H options to printf to output strings with <"'&\t> properly - converted for use in HTML and XML documents. -00-04-07 Modified getopts builtin to handle \f...\f in usage string - by invoking specified function. -00-04-04 Added self generating man pages for bg, fc, fg, disown, jobs, - hist, let, ., and ulimit. -00-03-30 The append operator += has been added and can be used - for all assignments, strings, arrays, and compound variables. -00-03-30 Code was modified in several places to support automatic - generation of C locale dictionaries. -00-03-28 A bug in which the set and trap commands invoked with --name - type arguments would terminate the invoking script has - been fixed. -00-03-27 A bug in which the library path variable was not updated - correctly on some systems as described in the 'g' point - release has been fixed. -00-03-07 printf now returns a non-zero exit status when one of - its arguments cannot be converted to the given type. -00-03-05 The return value and error message for a command that - was found on the path but was not executable was set - incorrectly. -00-03-05 A prototype for ioctl() was removed from the vi edit mode. - -00-01-28 --- Release ksh93i --- -00-01-28 Most of the built-in commands and ksh itself are now - self documenting. Running command --man will produce - screen output. Running command --html produces the - man page in html format. -00-01-28 The getopts builtin can process command description - strings to produce man pages. -00-01-28 A bug in which a script could terminate when getopts - encountered an error when invoked inside a function - has been fixed. -00-01-28 When a symbolic link was specified as the name of - the script to invoke by name, the value of $0 was - set to the real file name rather than the link name - in some cases and this has been fixed. -00-01-28 A bug in which the precision given as an argument - to printf was not working has been fixed. - -99-03-31 --- Release ksh93h --- -99-03-31 The PATH search algorithm has been modified to look - for a file named .fpath in each bin directory and if - found, to search for functions in this directory if - it cannot find the command in that directory. -99-03-31 When performing pathname expansion, the shell checks - to see whether each directory it reads is case sensitive - or not, and performs the matching accordingly. -99-03-31 The %T format for printing formatted date/time. -99-03-31 The emacs and vi modes now handle arrow keys when - they use standard ANSI escape sequences. -99-03-31 The TAB key can be used for completion in emacs and viraw mode. -99-03-31 A bug in setting .sh.editchar during the KEYBD trap - for the MULTIBYTE option was fixed in release ksh93h. -99-03-31 A bug in shcomp for compilation of unary operators with [[...]] - has been fixed. -99-03-31 A bug in which the value of $? was changed when executing - a keyboard trap has been fixed. -99-03-31 The handling of SIGCHLD has been changed so that the - trap is not triggered while executing trap commands - to avoid recursive trap calls. -99-03-31 A bug in which a local variable in a function declared readonly - would generated an error when the function went out of - scope has been fixed. -99-03-31 A bug in which \<new_line> entered from the keyboard - with the KEYBD trap enabled has been fixed. -99-03-31 The error message for a misplaced ((, for example print ((3), - was often garbled and has been fixed. -99-03-31 A bug in the KEYBD trap in which escape sequences of the form - <ESC>[#~ were not being handled as a unit has been fixed. -99-03-31 A bug in which ksh would consider expressions like [[ (a) ]] - as syntax errors has been fixed. -99-03-31 A function defined as foo() without a function body - was not reported as a syntax error. -99-03-31 A bug in which ksh could run out of file descriptors when - a stream was repeatedly opened with exec and read from - has been fixed. - -98-04-30 --- Release ksh93g --- -98-04-30 The pipefail option has been added. With pipefail - enabled, a pipeline will not complete until all - commands are complete, and the return value will - be that of the last command to fail, or zero if - all complete successfully. -98-04-30 The name-value pair library uses the cdt library rather - than the hash library. This change should be transparent - to applications. -98-04-30 On the U/WIN version for Window 95 and Windows NT, - when a directory beginning with a letter followed by - a colon is given to cd, it is assumed to be an absolute - directory -98-04-30 When an executable is found on a given path, - the appropriate library path variable is prepended - with a corresponding library directory. -98-04-30 A bug in which a name reference could be created to - itself and later cause the shell to get into an infinite - loop has been fixed. -98-04-30 A bug in shcomp relating to compound variables was fixed. -98-04-30 A bug introduced in ksh93e in which leading 0's in -Z - fields caused the value to be treated as octal for arithmetic - evaluation has been fixed. -98-04-30 A bug when a name reference with a shorter name than - the variable it references was the subject of a compound - assignment has been fixed. -98-04-30 A bug which in which assignment to array variables in - a subshell could effect the parent shell has been - fixed. -98-04-30 read name?prompt was putting a 0 byte at the end of the - prompt on standard error. -98-04-30 A bug in [[ string1 > string2 ]] when ksh was run with -x - has been fixed. -98-04-30 A bug in which the escape character was not processed - correctly inside {...} when brace expansion is enabled - has been fixed, for example {\$foo}. -98-04-30 A bug in line continuation in here-documents has been - fixed. -98-04-30 The default base when not specified with typeset -i is - 10 in accordance with the documentation. Previously, - the value was determined by the first assignment. -98-04-30 A parsing bug in which a # preceded alphanumeric - characters inside a command substitution caused - a syntax error to be reported has been fixed. -98-04-30 A bug in which a decimal constant represented as 10#ddd - where ddd was more than five digits generated a syntax - error has been fixed. -98-04-30 A bug in here document expansion in which ${...} expansions - were split across buffer boundaries has been fixed. -98-04-30 The sh_fun() function now takes third argument which - is an argument list for the invoked discipline function - or built-in. -98-04-30 A callback function can be installed which will give - notification of file duplications and file closes. -98-04-30 When ksh is compiled on systems that do not use fork() - current option settings where not propagated to sub-shells. - -97-06-30 --- Release ksh93f --- -97-06-30 Hostnames in addition to host addresses can be given in - /dev/tcp/host/port virtual file names. -97-06-30 File name completion and expansion now quotes special - characters in file names from both emacs and vi edit modes. -97-06-30 An empty for list behave like a for list with null expansions. - It produces a warning message with sh -n. -97-06-30 The code has been modified to work with EBCDIC as well as ASCII. -97-06-30 A bug which would cause the secondary prompt to be - displayed when a user entered a literal carriage - return has been fixed. -97-06-30 A bug which caused ksh read -s name to core dump was - fixed. -97-06-30 A bug with the expansion of \} and \] inside double - quoted strings that also contained variable expansions - has been fixed -97-06-30 Changes in the ksh93e point release caused autoload - functions invoked from within command substitution - to fail. This has been fixed. -97-06-30 A bug in the processing of here-documents that could - prevent variable substitution to occur after $(...) command - substitution for long here documents has been fixed. -97-06-30 A bug caused by a race condition that could cause SIGTERM - to be ignored by a child process has been fixed. -97-06-30 A bug which prevented the startup of a coprocess immediately - after killing a running coprocess has been fixed. -97-06-30 ulimit foobar, where foobar is not an arithmetic - expression, now gives an error message as it did with ksh88 - instead of setting the file size limit to 0. -97-06-30 A bug which could cause an interactive shell to terminate when - the last process of a pipeline was a POSIX function was fixed. -97-06-30 A bug which could cause command substitution of a shell script - to core dump has been fixed. -97-06-30 A security hole was fixed in suid_exec. -97-06-30 Arithmetic functions such as pow() that take more than - one argument, did not work if arguments other than the - first contained parenthesized sub-expression. -97-06-30 The error message from a script containing an incomplete - arithmetic expression has been corrected. -97-06-30 A bug which caused a core dump on some machines when - the value of a name reference contained a positional - parameter and the name reference was not defined inside - a function has been fixed. -97-06-30 Arithmetic expressions now correctly handle hexadecimal - constants. -97-06-30 A bug in which integer variables could be expanded - with a leading 10# when declared with typeset -i - multiple times has been corrected. -97-06-30 A bug in which IFS wasn't correctly restored when - set within command substitution has been fixed. -97-06-30 The _ character is now considered as part of a word - with the M-f and M-b emacs directives as it was in ksh88. -97-06-30 A bug in brace pattern expansions that caused expressions - such as {foo\,bar,bam} to expand incorrectly have been fixed. - - -96-07-31 --- Release ksh93e --- -96-07-31 The math functions, atan2, hypot, fmod, and pow were added. -96-07-31 When a shared library is loaded, if the function lib_init() - is defined in the library, it is invoked the first time that - the library is loaded with builtin -f library. -96-07-31 The k-shell information abstraction database option, KIA, - has been revamped. -96-07-31 Empty command substitutions of the form $() now work. - whence -v foo now gives the correct result after calling - builtin -d foo. -96-07-31 A bug in right to left arithmetic assignment for which - the arithmetic expression (( y = x = 1.5 )) did not - yield 1 for y when x was declared typeset -i was fixed. -96-07-31 printf has been fixed to handle format containing \0 - and/or \0145 correctly. In addition, characters following - %b in the format string are no longer displayed when - the operand contains \c. -96-07-31 A bug in printf that could cause the %E format to - produce unnormalized results has been fixed. -96-07-31 A bug which causes some arithmetic expressions to be - incorrectly evaluated as integer expressions rather - that floating point has been fixed. -96-07-31 Functions defined inside a subshell no longer remain - defined when the subshell completes. -96-07-31 The error message from sh -c ';echo foo' has been - corrected. -96-07-31 The format for umask -S has been changed to agree - with the specification in the POSIX standard. -96-07-31 A bug that caused side effects in subscript evaluation - when tracing was enabled for subscripts using ++ or -- - has been fixed. -96-07-31 To conform to the Posix standard getopts has been changed - so that the option char is set to ? when it returns with - a non-zero exit status. -96-07-31 The handling of \} inside ${name...} has been fixed so - that the \ quotes the }. -96-07-31 A bug that caused the read builtin to resume execution - after processing a trap has been fixed. -96-07-31 [[ -s file ]] has been fixed so that if file is open - by ksh, it is flushed first. -96-07-31 In some cases attributes and sizes for non exported - variables weren't being reset before running a script. -96-07-31 The value of TMOUT was affected by changes make to - it in a subshell. -96-07-31 The jobs command did not reflect changes make by - sending the CONT signal to a command. -96-07-31 The error message for ksh -o unknown was incorrect. -96-07-31 Functions invoked as name=value name, did not use - values from the calling scope when evaluating value. -96-07-31 A bug in which the shell would reexecute previously - executed code when a shell script or coprocess was - run in the background has been fixed. -96-07-31 A bug in which an empty here-document would leave - a file descriptor open has been fixed. -96-07-31 A bug in which $(set -A array ...) would leave a - side effect has been fixed. -96-07-31 A discipline function for a global variable defined - within a function defined with the function keyword, - incorrectly created a local variable of the same name - and applied the discipline to it. - -95-08-28 --- Release ksh93d --- -95-08-28 The \ character was not handled correctly in replacement - patterns with ${x/pattern/replace}. -95-08-28 A bug with read in which the line did not end with - a new-line has been fixed. -95-08-28 A bug in file name generation which sometimes - appended a . for filenames that ended in / has - been fixed. -95-08-28 If a process is waited for after a status has - been returned by a previous wait, wait now - returns 127. -95-08-28 A bug with hist (fc) -e which prevented a command - to re-executed after it had been edited has been fixed. -95-08-28 A bug which prevented quoting from removing the meaning - of unary test operators has been fixed. -95-08-28 A bug with typeahead and KEYBOARD traps with the - MULTIBYTE option set has been fixed. -95-08-28 Builtin functions can take a third argument which is - a void*. -95-08-28 The nv_scan() function can restrict the scope of a walk - to the top scope. - -95-04-31 --- Release ksh93c --- -95-04-31 The expansion of "$@" was incorrect when $1 was the null - string. -95-04-31 A bug which could incorrectly report a syntax error in - a backquoted expression when a $ was preceded by \\ - has been fixed. -95-04-31 A bug which prevented the shell from exiting after - reporting an error when failing to open a script - has been fixed. -95-04-31 A bug that could lead to memory corruption when a - large here document that required parameter or command - substitution was expanded has been fixed. -95-04-31 A bug that could cause a core dump on some systems - after ksh detected an error when reading a function - has been fixed. -95-04-31 A bug which could cause a coprocess to hang when - reading from a process that has terminated has been fixed. -95-04-31 A bug which caused a script to terminate when set -e - was on and the first command of and && or || list - failed has been fixed. -95-04-31 A bug with here documents inside $(...) when the delimiter - word is an identifier has been fixed. -95-04-31 A bug which caused $0 to display the wrong value when - a script was invoked as an argument to the . command - and the eval command has been fixed. -95-04-31 A bug that could cause the built-in sleep to hang - has been fixed. -95-04-31 A bug introduces in 12/28/93b which caused the backslash - to be removed when it was followed by digit inside double - quotes in some instances has been fixed. -95-04-31 A bug which could cause a core dump if ksh was invoked with - standard input closed has been fixed. -95-04-31 A bug which could cause a core dump if typeset -A was - specified for an existing variable has been fixed. -95-04-31 Variables that were unset but had attributes such as readonly - and export were not listed with readonly, export and typeset. -95-04-31 Several problems with signals have been fixed. -95-04-31 A bug which prevented ulimit -t from working has been fixed. - Also, a bug in which failed ulimits could cause a core dump - has also been fixed. -95-04-31 A bug in expansion of the form ${name/#pattern/string} and - ${name/%pattern/string} has been fixed. -95-04-31 A bug which caused read -r on a line that contained only - blanks to get a non-null value has been fixed. -95-04-31 A bug introduced in the 'a' point release in which - ${x='\\'} expanded to \ when x was unset has been fixed. -95-04-31 A bug which prevented a trap on EXIT from being executed - when the last command in a script was a function invocation - has been fixed. -95-04-31 A bug which caused an interactive shell ignore input when - standard error was redirected to a file with exec, - and then restored with exec 2>&1 has been fixed. -95-04-31 An interactive shell turns on monitor mode even when - standard error has been redirected to a file. -95-04-31 A bug which could cause standard input to be incorrectly - positioned for the last command of a script has been fixed. -95-04-31 A bug in the edit modes which allowed walking back in - the history file for more than HISTSIZE commands has - been fixed. -95-04-31 A bug which could cause a core dump if variable TMPDIR was - changed between two command substitutions has been fixed. -95-04-31. A bug which prevented a trap on EXIT from being cleared - has been fixed. -95-04-31 A bug fixed for the v directive in vi MULTIBYTE has been - fixed. -95-04-31 Code to for IFS handling of multibyte characters has - been added. -95-04-31 The displaying of multibyte strings in export, readonly, - typeset, and execution traces has been fixed. -95-04-31 Variables inside functions are now statically scoped. - The previous behavior was never documented. -95-04-31 Variables inside functions are now statically scoped. - The previous behavior was never documented. -95-04-31 A few changes have been made to the name-value library - that affect built-ins that use disciplines. The - changes allow disciplines to be shared by variables - and should make it possible to add new disciplines - without recompilation. -95-04-31 The name-value library interface has undergone significant - change for this revision. See the new nval.3 man page. - -94-12-31 --- Release ksh93b --- -94-12-31 Variables inside functions are now statically scoped. - The previous behavior was never documented. -94-12-31 If IFS contains two consecutive identical characters belonging - to the [:space:] class, then this character is treated as - a non-space delimiter so that each instance will delimit - a field. For example, IFS=$'\t\t' will cause two consecutive - tabs to delimit a null field. -94-12-31 The getopts command has a -a name option that specifies a - name that will be used for usage messages. -94-12-31 A bug which caused unset RANDOM to dump core has been - fixed. -94-12-31 A bug which prevented return for terminating a profile - or ENV file has been fixed. -94-12-31 A bug which prevented standard input from being - directed to /dev/null for background jobs when - monitor mode was turned off has been fixed. -94-12-31 Statements of the form typeset -options var[expr]=value - did not perform substitutions on expr as expected. -94-12-31 A bug which prevented the shell from sending a HUP - signal to some background jobs that were not disowned - has been fixed. -94-12-31 A bug which allowed a script to trap signals that are - ignored at the time that the shell was invoked by exec - has been fixed. -94-12-31 A bug which could cause a core dump when a discipline - function was unset within a discipline was fixed. -94-12-31 The typeset builtin now accepts a first argument of - + or - for compatibility with ksh88. -94-12-31 For compatibility with ksh88, the results of expansions - of command arguments will treat the extended character - match characters ()|& as ordinary characters. -94-12-31 A bug which caused read to fail on a file that was - open for read/write with <> when the first operation - was print or printf has been fixed. -94-12-31 When a job is suspended, it is put on the top of - the job list as required by the POSIX standard. -94-12-31 The value of OPTARG when an option that required - an argument but didn't have one was incorrect in the - case the the option string began with a :. -94-12-31 A bug which caused the terminal to get into a bad - state with some KEYBD traps in vi-mode has been fixed. -94-12-31 A bug which caused an invalid trap to cause a script - to terminate, rather than just return an error, has - been fixed. -94-12-31 Backreferencing sub-expressions in patterns and replacement - strings now works. -94-12-31 A bug in chmod which caused the -R option to fail has - been fixed. -94-12-31 More signal names have been added for Solaris - -94-06-30 --- Release ksh93a --- -94-06-30 An expansion bug which causes portions of a word after - a $((...)) expansion that contains a nested $var expansion - to be lost has been fixed. -94-06-30 A bug that caused a core dump when a script that did not - have PWD set and did a cd inside command substitution - has been fixed. -94-06-30 A bug which caused a core dump on some machines when - the LANG variable was assigned to has been fixed. -94-06-30 A bug which incorrectly handled set disciplines that - performed arithmetic evaluation when the discipline - was called from the arithmetic evaluator has been fixed. -94-06-30 A bug caused by an EXIT trap inside a function that - was executed in a subshell was fixed. -94-06-30 If foo is a function, and not a program, then command foo - now reports that foo isn't found rather than invoking foo. -94-06-30 The previous version incorrectly listed -A as an - invocation option. The -A option is only for set. -94-06-30 A bug was fixed which caused ksh to loop when execution trace - was enabled and the PS4 prompt required command substitution. -94-06-30 A bug which could cause the job control switch character - to be disabled when a script that enabled monitor mode - terminated was fixed. -94-06-30 A bug in the macro expansion global replacement operator //, - when the pattern began with a [ or +( has been fixed. -94-06-30 A bug which prevented ~ expansion from occurring when - it was terminated with a colon inside an assignment - has been fixed. -94-06-30 A bug in the dot command which prevented autoload functions - from working has been fixed. -94-06-30 A bug which caused a variable to be unset if the - its value were expanded inside a set discipline has - been fixed. -94-06-30 Whence -a now longer reports that a defined function - is undefined. -94-06-30 A bug on some systems in which $0 would be incorrect - in scripts invoked by name has been fixed. -94-06-30 Here documents with an empty body now work. -94-06-30 A bug which disabled argument passing and resetting - of options for a script invoked by name inside a - function has been fixed. -94-06-30 A bug in which an EXIT trap set the caller of a function - would be executed if a command called inside a function - was not found has been fixed. -94-06-30 A bug which allowed a script to trap signals that are - ignored at the time that the shell was invoked has - been fixed. -94-06-30 A bug which caused 2<&1- when applied to a shell built-in - to leave standard input closed has been fixed. -94-06-30 A bug which caused the shell to incorrectly parse - $() command substitutions with nested case statements - has been fixed. - diff --git a/usr/src/lib/libshell/common/TYPES b/usr/src/lib/libshell/common/TYPES deleted file mode 100644 index 6eb6f41b5e..0000000000 --- a/usr/src/lib/libshell/common/TYPES +++ /dev/null @@ -1,182 +0,0 @@ - -The ability for users to define types has been added to ksh93t. -Here is a quick summary of how types are defined and used in ksh93t. -This is still a work in progress so some changes and additions -are likely. - -A type can be defined either by a shared library or by using the new -typeset -T option to the shell. The method for defining types via -a shared library is not described here. However, the source file -bltins/enum.c is an example of a builtin that creates enumeration types. - -By convention, typenames begin with a capitol letter and end in _t. -To define a type, use - typeset -T Type_t=( - definition - ) -where definition contains assignment commands, declaration commands, -and function definitions. A declaration command (for example typeset, -readonly, and export), is a built-in that differs from other builtins in -that tilde substitution is performed on arguments after an =, assignments -do not have to precede the command name, and field splitting and pathname -expansion is not performed on the arguments. -For example, - typeset -T Pt_t=( - float -h 'length in inches' x=1 - float -h 'width in inches' y=0 - integer -S count=0 - len() - { - print -r $((sqrt(_.x*_.x + _.y*_.y))) - } - set() - { - (( _.count++)) - } - ) - -defines a type Pt_t that has three variables x, y, and count defined as well -as the discipline functions len and set. The variable x has an initial value -of 1 and the variable y has an initial value of 0. The new -h option argument, -is used for documentations purposes as described later and is ignored outside -of a type definition. - - -The variable count has the new -S attribute which means that it is shared -between all instances of the type. The -S option to typeset is ignored -outside of a type definition. Note the variable named _ that is used inside -the function definition for len and set. It will be a reference to the -instance of Pt_t that invoked the function. The functions len and set -could also have been defined with function len and function set, but -since there are no local variables, the len() and set() form are more -efficient since they don't need to set up a context for local variables -and for saving and restoring traps. - -If the discipline function named create is defined it will be -invoked when creating each instance for that type. A function named -create cannot be defined by any instance. - -When a type is defined, a declaration built-in command by this name -is added to ksh. As with other shell builtins, you can get the man page -for this newly added command by invoking Pt_t --man. The information from -the -h options will be embedded in this man page. Any functions that -use getopts to process arguments will be cross referenced on the generated -man page. - -Since Pt_t is now a declaration command it can be used in the definition -of other types, for example - typeset -T Rect_t=( Pt_t ur ll) - -Because a type definition is a command, it can be loaded on first reference -by putting the definition into a file that is found on FPATH. -Thus, if this definition is in a file named Pt_t on FPATH, then -a program can create instances of Pt_t without first including -the definition. - -A type definition is readonly and cannot be unset. Unsetting non-shared -elements of a type restores them to their default value. Unsetting a -shared element has no effect. - -The Pt_t command is used to create an instance of Pt_t. - Pt_t p1 -creates an instance named p1 with the initial value for p1.x set to 1 -and the initial value of p1.y set to 0. - Pt_t p2=(x=3 y=4) -creates an instance with the specified initial values. The len function -gives the distance of the point to the origin. Thus, p1.len will output -1 and p2.len will output 5. - -ksh93t also introduces a more efficient command substitution mechanism. -Instead of $(command), the new command substitution ${ command;} -can be used. Unlike (and ) which are always special, the { and } are -reserved words and require the space after { and a newline or ; before }. -Unlike $(), the ${ ;} command substitution executes the command in -the current shell context saving the need to save and restore -changes, therefore also allowing side effects. - -When trying to expand an element of a type, if the element does not exist, -ksh will look for a discipline function with that name and treat this as if -it were the ${ ;} command substitution. Thus, ${p1.len} is equivalent to -${ p1.len;} and within an arithmetic expression, p1.len will be expanded -via the new command substitution method. - -The type of any variable can be obtained from the new prefix -operator @. Thus, ${@p1} will output Pt_t. - -By default, each instance inherits all the discipline functions defined -by the type definition other than create. However, each instance can define -a function by the same name that will override this definition. -However, only discipline functions with the same name as those defined -by the type or the standard get, set, append, and unset disciplines -can be defined by each instance. - -Each instance of the type Pt_t behaves like a compound variable except -that only the variables defined by the type can be referenced or set. -Thus, p2.x=9 is valid, but p2.z=9 is not. Unless a set discipline function -does otherwise, the value of $p1 will be expanded to the form of a compound -variable that can be used for reinput into ksh. - -If the variables var1 and var2 are of the same type, then the assignment - var2=var1 -will create a copy of the variable var1 into var2. This is equivalent to - eval var2="$var1" -but is faster since the variable does not need to get expanded or reparsed. - -The type Pt_t can be referenced as if it were a variable using the name -.sh.type.Pt_t. To change the default point location for subsequent -instances of Pt_t, you can do - .sh.type.Pt_t=(x=5 y=12) -so that - Pt_t p3 - p3.len -would be 13. - -Types can be defined for simple variables as well as for compound -objects such as Pt_t. In this case, the variable named . inside -the definition refers to the real value for the variable. For example, -the type definition - typeset -T Time_t=( - integer .=0 - _='%H:%M:%S' - get() - { - .sh.value=$(printf "%(${_._})T" "#$((_))" ) - } - set() - { - .sh.value=$(printf "%(%#)T" "${.sh.value}") - - } - ) - -The sub-variable name _ is reserved for data used by discipline functions -and will not be included with data written with the %B option to printf. -In this case it is used to specify a date format. - -In this case - Time_t t1 t2=now -will define t1 as the time at the beginning of the epoch and t2 -as the current time. Unlike the previous case, $t2 will output -the current time in the date format specified by the value t2._. -However, the value of ${t2.} will expand the instance to a form -that can be used as input to the shell. - -Finally, types can be derived from an existing type. If the first -element in a type definition is named _, then the new type -consists of all the elements and discipline functions from the -type of _ extended by elements and discipline functions defined -by new type definition. For example, - - typeset -T Pq_t=( - Pt_t _ - float z=0. - len() - { - print -r $((sqrt(_.x*_.x + _.y*_.y + _.z*_.z))) - } - ) - -defines a new type Pq_t which is based on Pq_t and contains an additional -field z and a different len discipline function. It is also possible -to create a new type Pt_t based on the original Pt_t. In this case -the original Pt_t is no longer accessible. diff --git a/usr/src/lib/libshell/misc/images/callouts/1.png b/usr/src/lib/libshell/misc/images/callouts/1.png Binary files differdeleted file mode 100644 index 608fad3596..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/1.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/10.png b/usr/src/lib/libshell/misc/images/callouts/10.png Binary files differdeleted file mode 100644 index 39e55197cf..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/10.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/2.png b/usr/src/lib/libshell/misc/images/callouts/2.png Binary files differdeleted file mode 100644 index 5444738841..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/2.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/3.png b/usr/src/lib/libshell/misc/images/callouts/3.png Binary files differdeleted file mode 100644 index 64b87c7151..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/3.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/4.png b/usr/src/lib/libshell/misc/images/callouts/4.png Binary files differdeleted file mode 100644 index c308193ac4..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/4.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/5.png b/usr/src/lib/libshell/misc/images/callouts/5.png Binary files differdeleted file mode 100644 index 24799f0a43..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/5.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/6.png b/usr/src/lib/libshell/misc/images/callouts/6.png Binary files differdeleted file mode 100644 index 8919a670cd..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/6.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/7.png b/usr/src/lib/libshell/misc/images/callouts/7.png Binary files differdeleted file mode 100644 index e30e8a70cb..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/7.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/8.png b/usr/src/lib/libshell/misc/images/callouts/8.png Binary files differdeleted file mode 100644 index 3e35c8827c..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/8.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/callouts/9.png b/usr/src/lib/libshell/misc/images/callouts/9.png Binary files differdeleted file mode 100644 index ed2f14b4eb..0000000000 --- a/usr/src/lib/libshell/misc/images/callouts/9.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_bourne.png b/usr/src/lib/libshell/misc/images/tag_bourne.png Binary files differdeleted file mode 100644 index f1f78e3a25..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_bourne.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_i18n.png b/usr/src/lib/libshell/misc/images/tag_i18n.png Binary files differdeleted file mode 100644 index 559929df2a..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_i18n.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_ksh.png b/usr/src/lib/libshell/misc/images/tag_ksh.png Binary files differdeleted file mode 100644 index b33d5a7aa1..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_ksh.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_ksh88.png b/usr/src/lib/libshell/misc/images/tag_ksh88.png Binary files differdeleted file mode 100644 index d36dc0f5f5..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_ksh88.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_ksh93.png b/usr/src/lib/libshell/misc/images/tag_ksh93.png Binary files differdeleted file mode 100644 index 357ee3c50a..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_ksh93.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_l10n.png b/usr/src/lib/libshell/misc/images/tag_l10n.png Binary files differdeleted file mode 100644 index be89f7a163..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_l10n.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/images/tag_perf.png b/usr/src/lib/libshell/misc/images/tag_perf.png Binary files differdeleted file mode 100644 index fcb2960852..0000000000 --- a/usr/src/lib/libshell/misc/images/tag_perf.png +++ /dev/null diff --git a/usr/src/lib/libshell/misc/shell_styleguide.docbook b/usr/src/lib/libshell/misc/shell_styleguide.docbook deleted file mode 100644 index 74bc7501b5..0000000000 --- a/usr/src/lib/libshell/misc/shell_styleguide.docbook +++ /dev/null @@ -1,1464 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V5.0//EN" "http://www.oasis-open.org/docbook/xml/5.0b5/dtd/docbook.dtd" [ - <!ENTITY tag_bourneonly '<inlinemediaobject><imageobject><imagedata fileref="images/tag_bourne.png"></imagedata></imageobject><textobject><phrase>[Bourne]</phrase></textobject></inlinemediaobject> '> - <!ENTITY tag_kshonly '<inlinemediaobject><imageobject><imagedata fileref="images/tag_ksh.png"></imagedata></imageobject><textobject><phrase>[ksh]</phrase></textobject></inlinemediaobject> '> - <!ENTITY tag_ksh88only '<inlinemediaobject><imageobject><imagedata fileref="images/tag_ksh88.png"></imagedata></imageobject><textobject><phrase>[ksh88]</phrase></textobject></inlinemediaobject> '> - <!ENTITY tag_ksh93only '<inlinemediaobject><imageobject><imagedata fileref="images/tag_ksh93.png"></imagedata></imageobject><textobject><phrase>[ksh93]</phrase></textobject></inlinemediaobject> '> - <!ENTITY tag_performance '<inlinemediaobject><imageobject><imagedata fileref="images/tag_perf.png"></imagedata></imageobject><textobject><phrase>[perf]</phrase></textobject></inlinemediaobject> '> - <!ENTITY tag_i18n '<inlinemediaobject><imageobject><imagedata fileref="images/tag_i18n.png"></imagedata></imageobject><textobject><phrase>[i18n]</phrase></textobject></inlinemediaobject> '> - <!ENTITY tag_l10n '<inlinemediaobject><imageobject><imagedata fileref="images/tag_l10n.png"></imagedata></imageobject><textobject><phrase>[l10n]</phrase></textobject></inlinemediaobject> '> -]> -<!-- - - CDDL HEADER START - - The contents of this file are subject to the terms of the - Common Development and Distribution License (the "License"). - You may not use this file except in compliance with the License. - - You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - or http://www.opensolaris.org/os/licensing. - See the License for the specific language governing permissions - and limitations under the License. - - When distributing Covered Code, include this CDDL HEADER in each - file and include the License file at usr/src/OPENSOLARIS.LICENSE. - If applicable, add the following below this CDDL HEADER, with the - fields enclosed by brackets "[]" replaced with your own identifying - information: Portions Copyright [yyyy] [name of copyright owner] - - CDDL HEADER END - ---> - -<!-- - - Copyright 2009 Sun Microsystems, Inc. All rights reserved. - Use is subject to license terms. - ---> - -<!-- tag images were created like this: -$ (text="perf" ; - pbmtext -nomargins -lspace 0 -builtin fixed "${text}" | - pbmtopgm 1 1 | - pgmtoppm 1.0,1.0,1.0-0,0,0 /dev/stdin | - ppmtogif | - giftopnm | - pnmtopng >"tag_${text}.png") ---> - -<!-- compile with: -xsltproc −−stringparam generate.section.toc.level 0 \ - −−stringparam toc.max.depth 3 \ - −−stringparam toc.section.depth 12 \ - −−xinclude -o opensolaris_shell_styleguide.html /usr/share/sgml/docbook/docbook-xsl-stylesheets-1.69.1/html/docbook.xsl opensolaris_shell_styleguide.docbook ---> - -<article - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns="http://docbook.org/ns/docbook" - xml:lang="en"> - <!-- xmlns:xi="http://www.w3.org/2001/XInclude" --> - - <info> - <title><emphasis>[DRAFT]</emphasis> Bourne/Korn Shell Coding Conventions</title> - - <!-- subtitle abuse --> - <subtitle> - This page is currently work-in-progress until it is approved by the OS/Net community. Please send any comments to - <email>shell-discuss@opensolaris.org</email>. - </subtitle> - - - <authorgroup> -<!-- - <author><personname>David G. Korn</personname><email>dgk@research.att.com</email></author> - <author><personname>Roland Mainz</personname><email>roland.mainz@nrubsig.org</email></author> - <author><personname>Mike Shapiro</personname><email>mike.shapiro@sun.com</email></author> ---> - <author><orgname>OpenSolaris.org</orgname></author> - </authorgroup> - </info> - -<section xml:id="intro"> - <title>Intro</title> - <para>This document describes the shell coding style used for all the SMF script changes integrated into (Open)Solaris.</para> - <para>All new SMF shell code should conform to this coding standard, which is intended to match our existing C coding standard.</para> - <para>When in doubt, think "what would be the C-Style equivalent ?" and "What does the POSIX (shell) standard say ?"</para> -</section><!-- end of intro --> - - -<section xml:id="rules"> - <title>Rules</title> - - - - <section xml:id="general"> - <title>General</title> - - <section xml:id="basic_format"> - <title>Basic Format</title> - <para>Similar to <literal>cstyle</literal>, the basic format is that all - lines are indented by TABs or eight spaces, and continuation lines (which - in the shell end with "\") are indented by an equivalent number of TABs - and then an additional four spaces, e.g. -<programlisting> -cp foo bar -cp some_realllllllllllllllly_realllllllllllllly_long_path \ - to_another_really_long_path -</programlisting> - </para> - <para>The encoding used for the shell scripts is either <literal>ASCII</literal> - or <literal>UTF-8</literal>, alternative encodings are only allowed when the - application requires this.</para> - </section> - - - <section xml:id="commenting"> - <title>Commenting</title> - <para>Shell comments are preceded by the '<literal>#</literal>' character. Place - single-line comments in the right-hand margin. Use an extra '<literal>#</literal>' - above and below the comment in the case of multi-line comments: -<programlisting> -cp foo bar # Copy foo to bar - -# -# Modify the permissions on bar. We need to set them to root/sys -# in order to match the package prototype. -# -chown root bar -chgrp sys bar -</programlisting> - </para> - </section> - - - <section xml:id="interpreter_magic"> - <title>Interpreter magic</title> - <para>The proper interpreter magic for your shell script should be one of these: -<programlisting> -#!/bin/sh Standard Bourne shell script -#!/bin/ksh -p Standard Korn shell 88 script. You should always write ksh - scripts with -p so that ${ENV} (if set by the user) is not - sourced into your script by the shell. -#!/bin/ksh93 Standard Korn shell 93 script (-p is not needed since ${ENV} is - only used for interactive shell sessions). -</programlisting> - </para> - </section> - - - <section xml:id="harden_your_script_against_unexpected_input"> - <title>Harden the script against unexpected (user) input</title> - <para>Harden your script against unexpected (user) input, including - command line options, filenames with blanks (or other special - characters) in the name, or file input</para> - </section> - - - <section xml:id="use_builtin_commands"> - <title>&tag_kshonly;&tag_performance;Use builtin commands if the shell provides them</title> - <para> - Use builtin commands if the shell provides them. For example ksh93s+ - (ksh93, version 's+') delivered with Solaris (as defined by PSARC 2006/550) - supports the following builtins: - <simplelist type="inline"> - <member>basename</member> - <member>cat</member> - <member>chgrp</member> - <member>chmod</member> - <member>chown</member> - <member>cmp</member> - <member>comm</member> - <member>cp</member> - <member>cut</member> - <member>date</member> - <member>dirname</member> - <member>expr</member> - <member>fds</member> - <member>fmt</member> - <member>fold</member> - <member>getconf</member> - <member>head</member> - <member>id</member> - <member>join</member> - <member>ln</member> - <member>logname</member> - <member>mkdir</member> - <member>mkfifo</member> - <member>mv</member> - <member>paste</member> - <member>pathchk</member> - <member>rev</member> - <member>rm</member> - <member>rmdir</member> - <member>stty</member> - <member>tail</member> - <member>tee</member> - <member>tty</member> - <member>uname</member> - <member>uniq</member> - <member>wc</member> - <member>sync</member> - </simplelist> - Those builtins can be enabled via <literal>$ builtin name_of_builtin #</literal> in shell - scripts (note that ksh93 builtins implement exact POSIX behaviour - some - commands in Solaris <filename>/usr/bin/</filename> directory implement pre-POSIX behaviour. - Add <literal>/usr/xpg6/bin/:/usr/xpg4/bin</literal> before - <filename>/usr/bin/</filename> in <envar>${PATH}</envar> to test whether your script works with - the XPG6/POSIX versions) - </para> - </section> - - - <section xml:id="use_blocks_not_subshells"> - <title>&tag_performance;Use blocks and not subshells if possible</title> - <para>Use blocks and not subshells if possible, e.g. use - <literal>$ { print "foo" ; print "bar" ; }</literal> instead of - <literal>$ (print "foo" ; print "bar") #</literal> - blocks are - faster since they do not require to save the subshell context (ksh93) or - trigger a shell child process (Bourne shell, bash, ksh88 etc.) - </para> - </section> - - - <section xml:id="use_long_options_for_set_builtin"> - <title>&tag_kshonly; use long options for "<literal>set</literal>"</title> - <para>use long options for "<literal>set</literal>", for example instead of <literal>$ set -x #</literal> - use <literal>$ set -o xtrace #</literal> to make the code more readable.</para> - </section> - - - <section xml:id="use_posix_command_substitutions_syntax"> - <title>&tag_kshonly; Use <literal>$(...)</literal> instead of <literal>`...`</literal> command substitutions</title> - <para>Use <literal>$(...)</literal> instead of <literal>`...`</literal> - <literal>`...`</literal> - is an obsolete construct in ksh+POSIX sh scripts and <literal>$(...)</literal>.is a cleaner design, - requires no escaping rules, allows easy nesting etc.</para> - - <note><title>&tag_ksh93only; <literal>${ ...;}</literal>-style command substitutions</title> - <para>ksh93 has support for an alternative version of command substitutions with the - syntax <literal>${ ...;}</literal> which do not run in a subshell. - </para></note> - </section> - - - <section xml:id="put_command_substitution_result_in_quotes"> - <title>&tag_kshonly; Always put the result of a <literal>$(...)</literal> or - <literal>$( ...;)</literal> command substitution in quotes</title> - <para>Always put the result of <literal>$( ... )</literal> or <literal>$( ...;)</literal> in - quotes (e.g. <literal>foo="$( ... )"</literal> or <literal>foo="$( ...;)"</literal>) unless - there is a very good reason for not doing it</para> - </section> - - - <section xml:id="always_set_path"> - <title>Scripts should always set their <envar>PATH</envar></title> - <para>Scripts should always set their <envar>PATH</envar> to make sure they do not use - alternative commands by accident (unless the value of <envar>PATH</envar> is well-known - and guaranteed to be set by the caller)</para> - </section> - - - <section xml:id="make_sure_commands_are_available"> - <title>Make sure that commands from other packages/applications are really installed on the machine</title> - <para>Scripts should make sure that commands in optional packages are really - there, e.g. add a "precheck" block in scipts to avoid later failure when - doing the main job</para> - </section> - - - <section xml:id="check_usage_of_boolean_variables"> - <title>Check how boolean values are used/implemented in your application</title> - <para>Check how boolean values are used in your application.</para> - <para>For example: -<programlisting> -mybool=0 -# do something -if [ $mybool -eq 1 ] ; then do_something_1 ; fi -</programlisting> -could be rewritten like this: -<programlisting> -mybool=false # (valid values are "true" or "false", pointing -# to the builtin equivalents of /bin/true or /bin/false) -# do something -if ${mybool} ; then do_something_1 ; fi -</programlisting> -or -<programlisting> -integer mybool=0 # values are 0 or 1 -# do something -if (( mybool==1 )) ; then do_something_1 ; fi -</programlisting> - </para> - </section> - - <section xml:id="shell_uses_characters_not_bytes"> - <title>&tag_i18n;The shell always operates on <emphasis>characters</emphasis> not bytes</title> - <para>Shell scripts operate on characters and <emphasis>not</emphasis> bytes. - Some locales use multiple bytes (called "multibyte locales") to represent one character</para> - - <note><para>ksh93 has support for binary variables which explicitly - operate on bytes, not characters. This is the <emphasis>only</emphasis> allowed - exception.</para></note> - </section> - - - <section xml:id="multibyte_locale_input"> - <title>&tag_i18n;Multibyte locales and input</title> - <para>Think about whether your application has to handle file names or - variables in multibyte locales and make sure all commands used in your - script can handle such characters (e.g. lots of commands in Solaris's - <filename>/usr/bin/</filename> are <emphasis>not</emphasis> able to handle such values - either use ksh93 - builtin constructs (which are guaranteed to be multibyte-aware) or - commands from <filename>/usr/xpg4/bin/</filename> and/or <filename>/usr/xpg6/bin</filename>) - </para> - </section> - - - <section xml:id="use_external_filters_only_for_large_datasets"> - <title>&tag_performance;Only use external filters like <literal>grep</literal>/<literal>sed</literal>/<literal>awk</literal>/etc. - if you want to process lots of data with them</title> - <para>Only use external filters like <literal>grep</literal>/<literal>sed</literal>/<literal>awk</literal>/etc. - if a significant amount of data is processed by the filter or if - benchmarking shows that the use of builtin commands is significantly slower - (otherwise the time and resources needed to start the filter are - far greater then the amount of data being processed, - creating a performance problem).</para> - <para>For example: -<programlisting> -if [ "$(echo "$x" | egrep '.*foo.*')" != "" ] ; then - do_something ; -done -</programlisting> -can be re-written using ksh93 builtin constructs, saving several -<literal>|fork()|+|exec()|</literal>'s: -<programlisting> -if [[ "${x}" == ~(E).*foo.* ]] ; then - do_something ; -done -</programlisting> - </para> - </section> - - - <section xml:id="use_dashdash_if_first_arg_is_variable"> - <title>If the first operand of a command is a variable, use <literal>--</literal></title> - <para>If the first operand of a command is a variable, use <literal>--</literal> - for any command that accepts this as end of argument to - avoid problems if the variable expands to a value starting with <literal>-</literal>. - </para> - <note><para> - At least - <simplelist type="inline"> - <member>print</member> - <member>/usr/bin/fgrep</member> - <member>/usr/bin/grep</member> - <member>/usr/bin/egrep</member> - </simplelist> - support <literal>--</literal> as "end of arguments"-terminator. - </para></note> - </section> - - <section xml:id="use_export"> - <title>&tag_kshonly;&tag_performance;Use <literal>$ export FOOBAR=val #</literal> instead of - <literal>$ FOOBAR=val ; export FOOBAR #</literal></title> - <para>Use <literal>$ export FOOBAR=val # instead of $ FOOBAR=val ; export FOOBAR #</literal> - - this is much faster.</para> - </section> - - - <section xml:id="use_subshell_around_set_dashdash_usage"> - <title>Use a subshell (e.g. <literal>$ ( mycmd ) #</literal>) around places which use - <literal>set -- $(mycmd)</literal> and/or <literal>shift</literal></title> - <para>Use a subshell (e.g. <literal>$ ( mycmd ) #</literal>) around places which use - <literal>set -- $(mycmd)</literal> and/or <literal>shift</literal> unless the variable - affected is either a local one or if it's guaranteed that this variable will no longer be used - (be careful for loadable functions, e.g. ksh/ksh93's <literal>autoload</literal> !!!!) - </para> - </section> - - - <section xml:id="be_careful_with_tabs_in_script_code"> - <title>Be careful with using TABS in script code, they are not portable - between editors or platforms</title> - <para>Be careful with using TABS in script code, they are not portable - between editors or platforms.</para> - <para>If you use ksh93 use <literal>$'\t'</literal> to include TABs in sources, not the TAB character itself.</para> - </section> - - - <section xml:id="centralise_error_exit"> - <title>If you have multiple points where your application exits with an error - message create a central function for this purpose</title> - <para>If you have multiple points where your application exits with an error - message create a central function for this, e.g. -<programlisting> -if [ -z "$tmpdir" ] ; then - print -u2 "mktemp failed to produce output; aborting." - exit 1 -fi -if [ ! -d $tmpdir ] ; then - print -u2 "mktemp failed to create a directory; aborting." - exit 1 -fi -</programlisting> -should be replaced with -<programlisting> -function fatal_error -{ - print -u2 "${progname}: $*" - exit 1 -} -# do something (and save ARGV[0] to variable "progname") -if [ -z "$tmpdir" ] ; then - fatal_error "mktemp failed to produce output; aborting." -fi -if [ ! -d "$tmpdir" ] ; then - fatal_error "mktemp failed to create a directory; aborting." -fi -</programlisting> - </para> - </section> - - - <section xml:id="use_set_o_nounset"> - <title>&tag_kshonly; Think about using <literal>$ set -o nounset #</literal> by default</title> - <para>Think about using <literal>$ set -o nounset #</literal> by default (or at least during the - script's development phase) to catch errors where variables are used - when they are not set (yet), e.g. -<screen> -$ <userinput>(set -o nounset ; print ${foonotset})</userinput> -<computeroutput>/bin/ksh93: foonotset: parameter not set</computeroutput> -</screen> - </para> - </section> - - - <section xml:id="avoid_eval_builtin"> - <title>Avoid using <literal>eval</literal> unless absolutely necessary</title> - <para>Avoid using <literal>eval</literal> unless absolutely necessary. Subtle things - can happen when a string is passed back through the shell - parser. You can use name references to avoid uses such as - <literal>eval $name="$value"</literal>. - </para> - </section> - - - <section xml:id="use_concatenation_operator"> - <title>&tag_ksh93only;Use the string/array concatenation operator <literal>+=</literal></title> - <para>Use <literal>+=</literal> instead of manually adding strings/array elements, e.g. -<programlisting> -foo="" -foo="${foo}a" -foo="${foo}b" -foo="${foo}c" -</programlisting> -should be replaced with -<programlisting> -foo="" -foo+="a" -foo+="b" -foo+="c" -</programlisting> - </para> - </section> - - <section xml:id="use_source_not_dot"> - <title>&tag_ksh93only;Use <literal>source</literal> instead of '<literal>.</literal> '(dot) - to include other shell script fragments</title> - <para>Use <literal>source</literal> instead of '<literal>.</literal>' - (dot) to include other shell script fragments - the new form is much - more readable than the tiny dot and a failure can be caught within the script.</para> - </section> - - - <section xml:id="use_builtin_localisation_support"> - <title>&tag_ksh93only;&tag_performance;&tag_l10n;Use <literal>$"..."</literal> instead of - <literal>gettext ... "..."</literal> for strings that need to be localized for different locales</title> - <para>Use $"..." instead of <literal>gettext ... "..."</literal> for strings that need to be - localized for different locales. <literal>gettext</literal> will require a - <literal>fork()+exec()</literal> and - reads the whole catalog each time it's called, creating a huge overhead for localisation - (and the <literal>$"..."</literal> is easier to use, e.g. you only have to put a - <literal>$</literal> in front of the catalog and the string will be localised). - </para> - </section> - - - <section xml:id="use_set_o_noglob"> - <title>&tag_kshonly;&tag_performance;Use <literal>set -o noglob</literal> if you do not need to expand files</title> - <para>If you don't expect to expand files, you can do set <literal>-f</literal> - (<literal>set -o noglob</literal>) as well. This way the need to use <literal>""</literal> is - greatly reduced.</para> - </section> - - - <section xml:id="use_empty_ifs_to_handle_spaces"> - <title>&tag_ksh93only;Use <literal>IFS=</literal> to avoid problems with spaces in filenames</title> - <para>Unless you want to do word splitting, put <literal>IFS=</literal> - at the beginning of a command. This way spaces in - file names won't be a problem. You can do - <literal>IFS='delims' read -r</literal> line - to override <envar>IFS</envar> just for the <literal>read</literal> command. However, - you can't do this for the <literal>set</literal> builtin.</para> - </section> - - - <section xml:id="set_locale_when_comparing_against_localised_output"> - <title>Set the message locale if you process output of tools which may be localised</title> - <para>Set the message locale (<envar>LC_MESSAGES</envar>) if you process output of tools which may be localised</para> - <example><title>Set <envar>LC_MESSAGES</envar> when testing for specific outout of the <filename>/usr/bin/file</filename> utility:</title> -<programlisting> -# set french as default message locale -export LC_MESSAGES=fr_FR.UTF-8 - -... - -# test whether the file "/tmp" has the filetype "directory" or not -# we set LC_MESSAGES to "C" to ensure the returned message is in english -if [[ "$(LC_MESSAGES=C file /tmp)" = *directory ]] ; then - print "is a directory" -fi -</programlisting> - <note><para>The environment variable <envar>LC_ALL</envar> always - overrides any other <envar>LC_*</envar> environment variables - (and <envar>LANG</envar>, too), - including <envar>LC_MESSAGES</envar>. - if there is the chance that <envar>LC_ALL</envar> may be set - replace <envar>LC_MESSAGES</envar> with <envar>LC_ALL</envar> - in the example above.</para></note> - </example> - </section> - - <section xml:id="cleanup_after_yourself"> - <title>Cleanup after yourself.</title> - <para>Cleanup after yourself. For example ksh/ksh93 have an <literal>EXIT</literal> trap which - is very useful for this. - </para> - <note><para> - Note that the <literal>EXIT</literal> trap is executed for a subshell and each subshell - level can run it's own <literal>EXIT</literal> trap, for example -<screen> -$ <userinput>(trap "print bam" EXIT ; (trap "print snap" EXIT ; print "foo"))</userinput> -<computeroutput>foo -snap -bam</computeroutput> -</screen> - </para></note> - </section> - - <section xml:id="use_proper_exit_code"> - <title>Use a proper <literal>exit</literal> code</title> - <para>Explicitly set the exit code of a script, otherwise the exit code - from the last command executed will be used which may trigger problems - if the value is unexpected.</para> - </section> - - - <section xml:id="shell_lint"> - <title>&tag_ksh93only;Use <literal>shcomp -n scriptname.sh /dev/null</literal> to check for common errors</title> - <para>Use <literal>shcomp -n scriptname.sh /dev/null</literal> to - check for common problems (such as insecure, depreciated or ambiguous constructs) in shell scripts.</para> - </section> - </section><!-- end of general --> - - - - - - <section xml:id="functions"> - <title>Functions</title> - - <section xml:id="use_functions"> - <title>Use functions to break up your code</title> - <para>Use functions to break up your code into smaller, logical blocks.</para> - </section> - - <section xml:id="do_not_reserved_keywords_for_function_names"> - <title>Do not use function names which are reserved keywords in C/C++/JAVA or the POSIX shell standard</title> - <para>Do not use function names which are reserved keywords (or function names) in C/C++/JAVA or the POSIX shell standard - (to avoid confusion and/or future changes/updates to the shell language). - </para> - </section> - - <section xml:id="use_ksh_style_function_syntax"> - <title>&tag_kshonly;&tag_performance;Use ksh-style <literal>function</literal></title> - <para>It is <emphasis>highly</emphasis> recommended to use ksh style functions - (<literal>function foo { ... }</literal>) instead - of Bourne-style functions (<literal>foo() { ... }</literal>) if possible - (and local variables instead of spamming the global namespace).</para> - - <warning><para> - The difference between old-style Bourne functions and ksh functions is one of the major differences - between ksh88 and ksh93 - ksh88 allowed variables to be local for Bourne-style functions while ksh93 - conforms to the POSIX standard and will use a function-local scope for variables declared in - Bourne-style functions.</para> - <para>Example (note that "<literal>integer</literal>" is an alias for "<literal>typeset -li</literal>"): -<programlisting> -# new style function with local variable -$ ksh93 -c 'integer x=2 ; function foo { integer x=5 ; } ; print "x=$x" -; foo ; print "x=$x" ;' -x=2 -x=2 -# old style function with an attempt to create a local variable -$ ksh93 -c 'integer x=2 ; foo() { integer x=5 ; } ; print "x=$x" ; foo ; -print "x=$x" ;' -x=2 -x=5 -</programlisting> - - <uri xlink:href="http://www.opensolaris.org/os/project/ksh93-integration/docs/ksh93r/general/compatibility/">usr/src/lib/libshell/common/COMPATIBILITY</uri> - says about this issue: -<blockquote><para> -Functions, defined with name() with ksh-93 are compatible with -the POSIX standard, not with ksh-88. No local variables are -permitted, and there is no separate scope. Functions defined -with the function name syntax, maintain compatibility. -This also affects function traces. -</para></blockquote> -(this issue also affects <filename>/usr/xpg4/bin/sh</filename> in Solaris 10 because it is based on ksh88. This is a bug.). - </para></warning> - - </section> - - - <section xml:id="use_proper_return_code"> - <title>Use a proper <literal>return</literal> code</title> - <para>Explicitly set the return code of a function - otherwise the exit code - from the last command executed will be used which may trigger problems - if the value is unexpected.</para> - <para>The only allowed exception is if a function uses the shell's <literal>errexit</literal> mode to leave - a function, subshell or the script if a command returns a non-zero exit code. - </para> - </section> - - <section xml:id="use_fpath_to_load_common_code"> - <title>&tag_kshonly;Use <envar>FPATH</envar> to load common functions, not <literal>source</literal></title> - <para> - Use the ksh <envar>FPATH</envar> (function path) feature to load functions which are shared between scripts - and not <literal>source</literal> - this allows to load such a function on demand and not all at once.</para> - </section> - - </section><!-- end of functions --> - - - - - <section xml:id="if_for_while"> - <title><literal>if</literal>, <literal>for</literal> and <literal>while</literal></title> - - <section xml:id="if_for_while_format"> - <title>Format</title> - <para>To match <literal>cstyle</literal>, the shell token equivalent to the <literal>C</literal> - "<literal>{</literal>" should appear on the same line, separated by a - "<literal>;</literal>", as in: -<programlisting> -if [ "$x" = "hello" ] ; then - echo $x -fi - -if [[ "$x" = "hello" ]] ; then - print $x -fi - -for i in 1 2 3; do - echo $i -done - -for ((i=0 ; i < 3 ; i++)); do - print $i -done - -while [ $# -gt 0 ]; do - echo $1 - shift -done - -while (( $# > 0 )); do - print $1 - shift -done -</programlisting> - </para> - </section> - - - <section xml:id="test_builtin"> - <title><literal>test</literal> Builtin</title> - <para>DO NOT use the test builtin. Sorry, executive decision.</para> - <para>In our Bourne shell, the <literal>test</literal> built-in is the same as the "[" - builtin (if you don't believe me, try "type test" or refer to <filename>usr/src/cmd/sh/msg.c</filename>).</para> - <para> - So please do not write: -<programlisting> -if test $# -gt 0 ; then -</programlisting> -instead use: -<programlisting> -if [ $# -gt 0 ] ; then -</programlisting> - </para> - </section> - - - <section xml:id="use_ksh_test_syntax"> - <title>&tag_kshonly;&tag_performance;Use "<literal>[[ expr ]]</literal>" instead of "<literal>[ expr ]</literal>"</title> - <para>Use "<literal>[[ expr ]]</literal>" instead of "<literal>[ expr ]</literal>" if possible - since it avoids going through the whole pattern expansion/etc. machinery and - adds additional operators not available in the Bourne shell, such as short-circuit - <literal>&&</literal> and <literal>||</literal>. - </para> - </section> - - - <section xml:id="use_posix_arithmetic_expressions"> - <title>&tag_kshonly; Use "<literal>(( ... ))</literal>" for arithmetic expressions</title> - <para>Use "<literal>(( ... ))</literal>" instead of "<literal>[ expr ]</literal>" - or "<literal>[[ expr ]]</literal>" expressions. - </para> - <para> - Example: Replace -<programlisting> -i=5 -# do something -if [ $i -gt 5 ] ; then -</programlisting> -with -<programlisting> -i=5 -# do something -if (( i > 5 )) ; then -</programlisting> - </para> - </section> - - - <section xml:id="compare_exit_code_using_math"> - <title>&tag_kshonly;&tag_performance;Compare exit code using arithmetic expressions expressions</title> - <para>Use POSIX arithmetic expressions to test for exit/return codes of commands and functions. - For example turn -<programlisting> -if [ $? -gt 0 ] ; then -</programlisting> -into -<programlisting> -if (( $? > 0 )) ; then -</programlisting> - </para> - </section> - - - <section xml:id="use_builtin_commands_in_loops"> - <title>&tag_bourneonly; Use builtin commands in conditions for <literal>while</literal> endless loops</title> - <para>Make sure that your shell has a "<literal>true</literal>" builtin (like ksh93) when - executing endless loops like <literal>$ while true ; do do_something ; done #</literal> - - otherwise each loop cycle runs a <literal>|fork()|+|exec()|</literal>-cycle to run - <filename>/bin/true</filename> - </para> - </section> - - - <section xml:id="single_line_if_statements"> - <title>Single-line if-statements</title> - <para>It is permissible to use <literal>&&</literal> and <literal>||</literal> to construct - shorthand for an "<literal>if</literal>" statement in the case where the if statement has a - single consequent line: -<programlisting> -[ $# -eq 0 ] && exit 0 -</programlisting> -instead of the longer: -<programlisting> -if [ $# -eq 0 ]; then - exit 0 -fi -</programlisting> - </para> - </section> - - - <section xml:id="exit_status_and_if_for_while"> - <title>Exit Status and <literal>if</literal>/<literal>while</literal> statements</title> - <para>Recall that "<literal>if</literal>" and "<literal>while</literal>" - operate on the exit status of the statement - to be executed. In the shell, zero (0) means true and non-zero means false. - The exit status of the last command which was executed is available in the $? - variable. When using "<literal>if</literal>" and "<literal>while</literal>", - it is typically not necessary to use - <literal>$?</literal> explicitly, as in: -<programlisting> -grep foo /etc/passwd >/dev/null 2>&1 -if [ $? -eq 0 ]; then - echo "found" -fi -</programlisting> -Instead, you can more concisely write: -<programlisting> -if grep foo /etc/passwd >/dev/null 2>&1; then - echo "found" -fi -</programlisting> -Or, when appropriate: -<programlisting> -grep foo /etc/passwd >/dev/null 2>&1 && echo "found" -</programlisting> - </para> - </section> - - </section><!-- end of if/for/while --> - - - - - - - <section xml:id="variables"> - <title>Variable types, naming and usage</title> - - <section xml:id="names_should_be_lowercase"> - <title>Names of local, non-environment, non-constant variables should be lowercase</title> - <para>Names of variables local to the current script which are not exported to the environment - should be lowercase while variable names which are exported to the - environment should be uppercase.</para> - <para>The only exception are global constants (=global readonly variables, - e.g. <literal>$ float -r M_PI=3.14159265358979323846 #</literal> (taken from <math.h>)) - which may be allowed to use uppercase names, too. - </para> - - <warning><para> - Uppercase variable names should be avoided because there is a good chance - of naming collisions with either special variable names used by the shell - (e.g. <literal>PWD</literal>, <literal>SECONDS</literal> etc.). - </para></warning> - </section> - - <section xml:id="do_not_reserved_keywords_for_variable_names"> - <title>Do not use variable names which are reserved keywords/variable names in C/C++/JAVA or the POSIX shell standard</title> - <para>Do not use variable names which are reserved keywords in C/C++/JAVA or the POSIX shell standard - (to avoid confusion and/or future changes/updates to the shell language). - </para> - <note> - <para>The Korn Shell and the POSIX shell standard have many more - reserved variable names than the original Bourne shell. All - these reserved variable names are spelled uppercase. - </para> - </note> - </section> - - <section xml:id="use_brackets_around_long_names"> - <title>Always use <literal>'{'</literal>+<literal>'}'</literal> when using variable - names longer than one character</title> - <para>Always use <literal>'{'</literal>+<literal>'}'</literal> when using - variable names longer than one character unless a simple variable name is - followed by a blank, <literal>/</literal>, <literal>;</literal>, or <literal>$</literal> - character (to avoid problems with array, - compound variables or accidental misinterpretation by users/shell) -<programlisting> -print "$foo=info" -</programlisting> -should be rewritten to -<programlisting> -print "${foo}=info" -</programlisting> - </para> - </section> - - - <section xml:id="quote_variables_containing_filenames_or_userinput"> - <title><emphasis>Always</emphasis> put variables into quotes when handling filenames or user input</title> - <para><emphasis>Always</emphasis> put variables into quotes when handling filenames or user input, even if - the values are hardcoded or the values appear to be fixed. Otherwise at - least two things may go wrong: - <itemizedlist> - <listitem><para>a malicious user may be able to exploit a script's inner working to - infect his/her own code</para></listitem> - <listitem><para>a script may (fatally) misbehave for unexpected input (e.g. file names - with blanks and/or special symbols which are interpreted by the shell)</para></listitem> - </itemizedlist> - </para> - - <note><para> - As alternative a script may set <literal>IFS='' ; set -o noglob</literal> to turn off the - interpretation of any field seperators and the pattern globbing. - </para></note> - </section> - - - - <section xml:id="use_typed_variables"> - <title>&tag_kshonly;&tag_performance;Use typed variables if possible.</title> - <para>For example the following is very - inefficient since it transforms the integer values to strings and back - several times: -<programlisting> -a=0 -b=1 -c=2 -# more code -if [ $a -lt 5 -o $b -gt c ] ; then do_something ; fi -</programlisting> -This could be rewritten using ksh constructs: -<programlisting> -integer a=0 -integer b=1 -integer c=2 -# more code -if (( a < 5 || b > c )) ; then do_something ; fi -</programlisting> - </para> - </section> - - - <section xml:id="store_lists_in_arrays"> - <title>&tag_ksh93only; Store lists in arrays or associative arrays</title> - <para>Store lists in arrays or associative arrays - this is usually easier - to manage.</para> - <para> - For example: -<programlisting> -x=" -/etc/foo -/etc/bar -/etc/baz -" -echo $x -</programlisting> -can be replaced with -<programlisting> -typeset -a mylist -mylist[0]="/etc/foo" -mylist[1]="/etc/bar" -mylist[2]="/etc/baz" -print "${mylist[@]}" -</programlisting> -or (ksh93-style append entries to a normal (non-associative) array) -<programlisting> -typeset -a mylist -mylist+=( "/etc/foo" ) -mylist+=( "/etc/bar" ) -mylist+=( "/etc/baz" ) -print "${mylist[@]}" -</programlisting> - </para> - <note> - <title>Difference between expanding arrays with mylist[@] and mylist[*] subscript operators</title> - <para> - Arrays may be expanded using two similar subscript operators, @ and *. These subscripts - differ only when the variable expansion appears within double quotes. If the variable expansion - is between double-quotes, "${mylist[*]}" expands to a single string with the value of each array - member separated by the first character of the <envar>IFS</envar> variable, and "${mylist[@]}" - expands each element of name to a separate string. - </para> - <example><title>Difference between [@] and [*] when expanding arrays</title> -<programlisting> -typeset -a mylist -mylist+=( "/etc/foo" ) -mylist+=( "/etc/bar" ) -mylist+=( "/etc/baz" ) -IFS="," -printf "mylist[*]={ 0=|%s| 1=|%s| 2=|%s| 3=|%s| }\n" "${mylist[*]}" -printf "mylist[@]={ 0=|%s| 1=|%s| 2=|%s| 3=|%s| }\n" "${mylist[@]}" -</programlisting> -<para>will print:</para> -<screen> -<computeroutput>mylist[*]={ 0=|/etc/foo,/etc/bar,/etc/baz| 1=|| 2=|| 3=|| } -mylist[@]={ 0=|/etc/foo| 1=|/etc/bar| 2=|/etc/baz| 3=|| } -</computeroutput> -</screen> - </example> - </note> - </section> - - - <section xml:id="use_compound_variables_or_lists_for_grouping"> - <title>&tag_ksh93only; Use compound variables or associative arrays to group similar variables together</title> - <para>Use compound variables or associative arrays to group similar variables together.</para> - <para> - For example: -<programlisting> -box_width=56 -box_height=10 -box_depth=19 -echo "${box_width} ${box_height} ${box_depth}" -</programlisting> -could be rewritten to ("associative array"-style) -<programlisting> -typeset -A -E box=( [width]=56 [height]=10 [depth]=19 ) -print -- "${box[width]} ${box[height]} ${box[depth]}" -</programlisting> -or ("compound variable"-style -<programlisting> -box=( - float width=56 - float height=10 - float depth=19 - ) -print -- "${box.width} ${box.height} ${box.depth}" -</programlisting> - </para> - </section> - </section><!-- end of variables --> - - - - - - - - <section xml:id="io"> - <title>I/O</title> - - <section xml:id="avoid_echo"> - <title>Avoid using the "<literal>echo</literal>" command for output</title> - <para>The behaviour of "<literal>echo</literal>" is not portable - (e.g. System V, BSD, UCB and ksh93/bash shell builtin versions all - slightly differ in functionality) and should be avoided if possible. - POSIX defines the "<literal>printf</literal>" command as replacement - which provides more flexible and portable behaviour.</para> - - <note> - <title>&tag_kshonly;Use "<literal>print</literal>" and not "<literal>echo</literal>" in Korn Shell scripts</title> - <para>Korn shell scripts should prefer the "<literal>print</literal>" - builtin which was introduced as replacement for "<literal>echo</literal>".</para> - <caution> - <para>Use <literal>$ print -- ${varname}" #</literal> when there is the slightest chance that the - variable "<literal>varname</literal>" may contain symbols like "-". Or better use "<literal>printf</literal>" - instead, for example -<programlisting> -integer fx -# do something -print $fx -</programlisting> -may fail if "f" contains a negative value. A better way may be to use -<programlisting> -integer fx -# do something -printf "%d\n" fx -</programlisting> - </para> - </caution> - </note> - </section> - - <section xml:id="use_redirect_not_exec_to_open_files"> - <title>&tag_ksh93only;Use <literal>redirect</literal> and not <literal>exec</literal> to open files</title> - <para>Use <literal>redirect</literal> and not <literal>exec</literal> to open files - <literal>exec</literal> - will terminate the current function or script if an error occurs while <literal>redirect</literal> - just returns a non-zero exit code which can be caught.</para> -<para>Example: -<programlisting> -if redirect 5</etc/profile ; then - print "file open ok" - head <&5 -else - print "could not open file" -fi -</programlisting> - </para> - </section> - - <section xml:id="group_identical_redirections_together"> - <title>&tag_performance;Avoid redirections per command when the output goes into the same file, - e.g. <literal>$ echo "foo" >xxx ; echo "bar" >>xxx ; echo "baz" >>xxx #</literal></title> - <para>Each of the redirections above trigger an - <literal>|open()|,|write()|,|close()|</literal>-sequence. It is much - more efficient (and faster) to group the rediction into a block, - e.g. <literal>{ echo "foo" ; echo "bar" ; echo "baz" } >xxx #</literal></para> - </section> - - - <section xml:id="avoid_using_temporary_files"> - <title>&tag_performance;Avoid the creation of temporary files and store the values in variables instead</title> - <para>Avoid the creation of temporary files and store the values in variables instead if possible</para> - <para> - Example: -<programlisting> -ls -1 >xxx -for i in $(cat xxx) ; do - do_something ; -done -</programlisting> -can be replaced with -<programlisting> -x="$(ls -1)" -for i in ${x} ; do - do_something ; -done -</programlisting> - </para> - <note><para>ksh93 supports binary variables (e.g. <literal>typeset -b varname</literal>) which can hold any value.</para></note> - </section> - - - <section xml:id="create_subdirs_for_multiple_temporary_files"> - <title>If you create more than one temporary file create an unique subdir</title> - <para>If you create more than one temporary file create an unique subdir for - these files and make sure the dir is writable. Make sure you cleanup - after yourself (unless you are debugging). - </para> - </section> - - - <section xml:id="use_dynamic_file_descriptors"> - <title>&tag_ksh93only;Use {n}<file instead of fixed file descriptor numbers</title> - <para>When opening a file use {n}<file, where <envar>n</envar> is an - integer variable rather than specifying a fixed descriptor number.</para> - <para>This is highly recommended in functions to avoid that fixed file - descriptor numbers interfere with the calling script.</para> -<example><title>Open a network connection and store the file descriptor number in a variable</title> -<programlisting> -function cat_http -{ - integer netfd - -... - - # open TCP channel - redirect {netfd}<>"/dev/tcp/${host}/${port}" - - # send HTTP request - request="GET /${path} HTTP/1.1\n" - request+="Host: ${host}\n" - request+="User-Agent: demo code/ksh93 (2007-08-30; $(uname -s -r -p))\n" - request+="Connection: close\n" - print "${request}\n" >&${netfd} - - # collect response and send it to stdout - cat <&${netfd} - - # close connection - exec {netfd}<&- - -... - -} -</programlisting> -</example> - </section> - - - <section xml:id="use_inline_here_documents"> - <title>&tag_ksh93only;&tag_performance;Use inline here documents - instead of <literal>echo "$x" | command</literal></title> - <para>Use inline here documents, for example -<programlisting> -command <<< $x -</programlisting> - rather than -<programlisting> -print -r -- "$x" | command -</programlisting> - </para> - </section> - - - <section xml:id="use_read_r"> - <title>&tag_ksh93only;Use the <literal>-r</literal> option of <literal>read</literal> to read a line</title> - <para>Use the <literal>-r</literal> option of <literal>read</literal> to read a line. - You never know when a line will end in <literal>\</literal> and without a - <literal>-r</literal> multiple - lines can be read.</para> - </section> - - - <section xml:id="print_compound_variables_using_print_C"> - <title>&tag_ksh93only;Print compound variables using <literal>print -C varname</literal> or <literal>print -v varname</literal></title> - <para>Print compound variables using <literal>print -C varname</literal> or - <literal>print -v varname</literal> to make sure that non-printable characters - are correctly encoded.</para> -<example><title>Print compound variable with non-printable characters</title> -<programlisting> -compound x=( - a=5 - b="hello" - c=( - d=9 - e="$(printf "1\v3")" <co xml:id="co.vertical_tab1" /> - ) -) -print -v x -</programlisting> -<para>will print:</para> -<screen> -<computeroutput>( - a=5 - b=hello - c=( - d=9 - e=$'1\0133' <co xml:id="co.vertical_tab2" /> - ) -)</computeroutput> -</screen> -<calloutlist> - <callout arearefs="co.vertical_tab1 co.vertical_tab2"> - <para>vertical tab, <literal>\v</literal>, octal=<literal>\013</literal>.</para> - </callout> -</calloutlist> -</example> - </section> - - <section xml:id="command_name_before_redirections"> - <title>Put the command name and arguments before redirections</title> - <para>Put the command name and arguments before redirections. - You can legally do <literal>$ > file date</literal> instead of <literal>date > file</literal> - but don't do it.</para> - </section> - - <section xml:id="enable_gmacs_editor_mode_for_user_prompts"> - <title>&tag_ksh93only;Enable the <literal>gmacs</literal> editor - mode when reading user input using the <literal>read</literal> builtin</title> - <para>Enable the <literal>gmacs</literal>editor mode before reading user - input using the <literal>read</literal> builtin to enable the use of - cursor+backspace+delete keys in the edit line</para> -<example><title>Prompt user for a string with gmacs editor mode enabled</title> -<programlisting> -set -o gmacs <co xml:id="co.enable_gmacs" /> -typeset inputstring="default value" -... -read -v<co xml:id="co.read_v" /> inputstring<co xml:id="co.readvar" />?"Please enter a string: "<co xml:id="co.prompt" /> -... -printf "The user entered the following string: '%s'\n" "${inputstring}" - -... -</programlisting> -<calloutlist> - <callout arearefs="co.enable_gmacs"> - <para>Enable gmacs editor mode.</para> - </callout> - <callout arearefs="co.read_v"> - <para>The value of the variable is displayed and used as a default value.</para> - </callout> - <callout arearefs="co.readvar"> - <para>Variable used to store the result.</para> - </callout> - <callout arearefs="co.prompt"> - <para>Prompt string which is displayed in stderr.</para> - </callout> -</calloutlist> -</example> - </section> - </section><!-- end of I/O --> - - - - - - - <section xml:id="math"> - <title>Math</title> - - <section xml:id="use_builtin_arithmetic_expressions"> - <title>&tag_kshonly;&tag_performance;Use builtin arithmetic expressions instead of external applications</title> - <para>Use builtin (POSIX shell) arithmetic expressions instead of - <filename>expr</filename>, - <filename>bc</filename>, - <filename>dc</filename>, - <filename>awk</filename>, - <filename>nawk</filename> or - <filename>perl</filename>. - </para> - <note> - <para>ksh93 supports C99-like floating-point arithmetic including special values - such as - <simplelist type="inline"> - <member>+Inf</member> - <member>-Inf</member> - <member>+NaN</member> - <member>-NaN</member> - </simplelist>. - </para> - </note> - </section> - - - <section xml:id="use_floating_point_arithmetic_expressions"> - <title>&tag_ksh93only; Use floating-point arithmetic expressions if - calculations may trigger a division by zero or other exceptions</title> - <para>Use floating-point arithmetic expressions if calculations may - trigger a division by zero or other exceptions - floating point arithmetic expressions in - ksh93 support special values such as <literal>+Inf</literal>/<literal>-Inf</literal> and - <literal>+NaN</literal>/<literal>-NaN</literal> which can greatly simplify testing for - error conditions, e.g. instead of a <literal>trap</literal> or explicit - <literal>if ... then... else</literal> checks for every sub-expression - you can check the results for such special values. - </para> - <para>Example: -<screen> -$ <userinput>ksh93 -c 'integer i=0 j=5 ; print -- "x=$((j/i)) "'</userinput> -<computeroutput>ksh93: line 1: j/i: divide by zero</computeroutput> -$ <userinput>ksh93 -c 'float i=0 j=-5 ; print -- "x=$((j/i)) "'</userinput> -<computeroutput>x=-Inf</computeroutput> -</screen> - </para> - </section> - - - <section xml:id="use_printf_a_for_passing_float_values"> - <title>&tag_ksh93only; Use <literal>printf "%a"</literal> when passing floating-point values</title> - <para>Use <literal>printf "%a"</literal> when passing floating-point values between scripts or - as output of a function to avoid rounding errors when converting between - bases.</para> - <para> - Example: -<programlisting> -function xxx -{ - float val - - (( val=sin(5.) )) - printf "%a\n" val -} -float out -(( out=$(xxx) )) -xxx -print -- $out -</programlisting> -This will print: -<programlisting> --0.9589242747 --0x1.eaf81f5e09933226af13e5563bc6p-01 -</programlisting> - </para> - </section> - - - <section xml:id="put_constants_into_readonly_variables"> - <title>&tag_kshonly;&tag_performance;Put constant values into readonly variables</title> - <para>Put constant values into readonly variables</para> - <para>For example: -<programlisting> -float -r M_PI=3.14159265358979323846 -</programlisting> -or -<programlisting> -float M_PI=3.14159265358979323846 -readonly M_PI -</programlisting> - </para> - </section> - - - <section xml:id="avoid_unnecessary_string_number_conversions"> - <title>&tag_kshonly;&tag_performance;Avoid string to number - (and/or number to string) conversions in arithmetic expressions - expressions</title> - <para>Avoid string to number and/or number to string conversions in - arithmetic expressions expressions to avoid performance degradation - and rounding errors.</para> - <example><title>(( x=$x*2 )) vs. (( x=x*2 ))</title> -<programlisting> -float x -... -(( x=$x*2 )) -</programlisting> -<para> -will convert the variable "x" (stored in the machine's native -<literal>|long double|</literal> datatype) to a string value in base10 format, -apply pattern expansion (globbing), then insert this string into the -arithmetic expressions and parse the value which converts it into the internal |long double| datatype format again. -This is both slow and generates rounding errors when converting the floating-point value between -the internal base2 and the base10 representation of the string. -</para> -<para> -The correct usage would be: -</para> -<programlisting> -float x -... -(( x=x*2 )) -</programlisting> -<para> -e.g. omit the '$' because it's (at least) redundant within arithmetic expressions. -</para> - </example> - - - <example><title>x=$(( y+5.5 )) vs. (( x=y+5.5 ))</title> -<programlisting> -float x -float y=7.1 -... -x=$(( y+5.5 )) -</programlisting> -<para> -will calculate the value of <literal>y+5.5</literal>, convert it to a -base-10 string value amd assign the value to the floating-point variable -<literal>x</literal> again which will convert the string value back to the -internal |long double| datatype format again. -</para> -<para> -The correct usage would be: -</para> -<programlisting> -float x -float y=7.1 -... -(( x=y+5.5 )) -</programlisting> -<para> -i.e. this will save the string conversions and avoid any base2-->base10-->base2-conversions. -</para> - </example> - </section> - - - <section xml:id="set_lc_numeric_when_using_floating_point"> - <title>&tag_ksh93only;Set <envar>LC_NUMERIC</envar> when using floating-point constants</title> - <para>Set <envar>LC_NUMERIC</envar> when using floating-point constants to avoid problems with radix-point - representations which differ from the representation used in the script, for example the <literal>de_DE.*</literal> locale - use ',' instead of '.' as default radix point symbol.</para> - <para>For example: -<programlisting> -# Make sure all math stuff runs in the "C" locale to avoid problems with alternative -# radix point representations (e.g. ',' instead of '.' in de_DE.*-locales). This -# needs to be set _before_ any floating-point constants are defined in this script) -if [[ "${LC_ALL}" != "" ]] ; then - export \ - LC_MONETARY="${LC_ALL}" \ - LC_MESSAGES="${LC_ALL}" \ - LC_COLLATE="${LC_ALL}" \ - LC_CTYPE="${LC_ALL}" - unset LC_ALL -fi -export LC_NUMERIC=C -... -float -r M_PI=3.14159265358979323846 -</programlisting> - </para> - - <note><para>The environment variable <envar>LC_ALL</envar> always overrides all other <envar>LC_*</envar> variables, - including <envar>LC_NUMERIC</envar>. The script should always protect itself against custom <envar>LC_NUMERIC</envar> and - <envar>LC_ALL</envar> values as shown in the example above. - </para></note> - </section> - - - - </section><!-- end of math --> - - - - - - - <section xml:id="misc"> - <title>Misc</title> - - <section xml:id="debug_use_lineno_in_ps4"> - <title>Put <literal>[${LINENO}]</literal> in your <envar>PS4</envar></title> - <para>Put <literal>[${LINENO}]</literal> in your <envar>PS4</envar> prompt so that you will get line - numbers with you run with <literal>-x</literal>. If you are looking at performance - issues put <literal>$SECONDS</literal> in the <envar>PS4</envar> prompt as well.</para> - </section> - - </section><!-- end of misc --> - - - - -</section><!-- end of RULES --> - - - - -</article> diff --git a/usr/src/lib/libsmartsshd/Makefile b/usr/src/lib/libsmartsshd/Makefile new file mode 100644 index 0000000000..50d9545ef1 --- /dev/null +++ b/usr/src/lib/libsmartsshd/Makefile @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.lib + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +_msg: + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libsmartsshd/Makefile.com b/usr/src/lib/libsmartsshd/Makefile.com new file mode 100644 index 0000000000..b9c5b88f23 --- /dev/null +++ b/usr/src/lib/libsmartsshd/Makefile.com @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY= libsmartsshd.a +VERS= .1 +OBJECTS= sshd-plugin.o + +include ../../Makefile.lib +include ../../Makefile.rootfs + +SRCDIR = ../common +SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c) + +CPPFLAGS += -I$(SRCDIR) -D_REENTRANT -D_FILE_OFFSET_BITS=64 +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lmd5 + +$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libsmartsshd/amd64/Makefile b/usr/src/lib/libsmartsshd/amd64/Makefile new file mode 100644 index 0000000000..31be0ef7e6 --- /dev/null +++ b/usr/src/lib/libsmartsshd/amd64/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libsmartsshd/common/llib-lsmartsshd b/usr/src/lib/libsmartsshd/common/llib-lsmartsshd new file mode 100644 index 0000000000..ace7017d41 --- /dev/null +++ b/usr/src/lib/libsmartsshd/common/llib-lsmartsshd @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/*LINTLIBRARY*/ +/*PROTOLIB1*/ +/* + * + * Copyright 2011 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + diff --git a/usr/src/lib/libsmartsshd/common/mapfile-vers b/usr/src/lib/libsmartsshd/common/mapfile-vers new file mode 100644 index 0000000000..c3c6fe4946 --- /dev/null +++ b/usr/src/lib/libsmartsshd/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2016 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate_1.1 { + global: + sshd_user_rsa_key_allowed; + sshd_user_dsa_key_allowed; + sshd_user_ecdsa_key_allowed; + sshd_user_key_allowed; + local: + *; +}; diff --git a/usr/src/lib/libsmartsshd/common/sshd-plugin.c b/usr/src/lib/libsmartsshd/common/sshd-plugin.c new file mode 100644 index 0000000000..a6463cea12 --- /dev/null +++ b/usr/src/lib/libsmartsshd/common/sshd-plugin.c @@ -0,0 +1,193 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +#include <alloca.h> +#include <door.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/rsa.h> + +#include <md5.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_OOM(SZ) (void) fprintf(stderr, "Cannot alloca %d bytes\n", SZ) + +static const char *DOOR = "/var/tmp/._joyent_sshd_key_is_authorized"; +static const char *REQ_FMT_STR = "%s %d %s"; /* name uid fp */ +static const int RETURN_SZ = 2; + +static const int MAX_ATTEMPTS = 2; +static const int SLEEP_PERIOD = 1; + +static int +sshd_allowed_in_capi(struct passwd *pw, const char *fp) +{ + int allowed = 0; + int fd = -1; + int blen = 0; + int attempts = 0; + char *buf = NULL; + door_arg_t door_args = {0}; + + if (pw == NULL || fp == NULL) + return (0); + + blen = snprintf(NULL, 0, REQ_FMT_STR, pw->pw_name, pw->pw_uid, fp) + 1; + + buf = (char *)alloca(blen); + if (buf == NULL) { + LOG_OOM(blen); + return (0); + } + + (void) snprintf(buf, blen, REQ_FMT_STR, pw->pw_name, pw->pw_uid, fp); + door_args.data_ptr = buf; + door_args.data_size = blen; + + door_args.rsize = RETURN_SZ; + door_args.rbuf = alloca(RETURN_SZ); + if (door_args.rbuf == NULL) { + LOG_OOM(RETURN_SZ); + return (0); + } + (void) memset(door_args.rbuf, 0, RETURN_SZ); + + do { + fd = open(DOOR, O_RDWR); + if (fd < 0) { + if (errno == ENOENT) { + /* + * On systems which are not running SmartLogin, + * such as vanilla SmartOS, the door will be + * completely absent. The sleep/retry loop is + * skipped in this case to keep the login + * process more lively. + */ + perror("smartplugin: door does not exist"); + return (0); + } + perror("smartplugin: open (of door FD) failed"); + } else if (door_call(fd, &door_args) < 0) { + perror("smartplugin: door_call failed"); + } else { + allowed = atoi(door_args.rbuf); + if (door_args.rsize > RETURN_SZ) { + /* + * Given what we know about the SmartLogin + * daemon on the other end of the door, this + * should never occur. An assert might be + * preferable, but that is avoided since the + * error can be handled. + */ + (void) munmap(door_args.rbuf, door_args.rsize); + } + return (allowed); + } + if (++attempts < MAX_ATTEMPTS) { + (void) sleep(SLEEP_PERIOD); + } + } while (attempts < MAX_ATTEMPTS); + + return (0); +} + +static int +tohexstr(uchar_t *bytes, size_t blen, char *hexstr, size_t hexlen) +{ + size_t i, j; + const char hexlist[] = "0123456789abcdef"; + + if (hexlen < 1) + return (-1); + for (i = 0, j = 0; i < blen; i++) { + /* + * We need 3 bytes output per input byte -- the third byte is + * either for a : or the \0 at the end. + */ + if (hexlen < (j + 3)) + return (-1); + hexstr[j++] = hexlist[(bytes[i] >> 4) & 0xf]; + hexstr[j++] = hexlist[bytes[i] & 0xf]; + if (i + 1 < blen) + hexstr[j++] = ':'; + } + hexstr[j] = '\0'; + return (0); +} + +/* ARGSUSED */ +int +sshd_user_key_allowed(struct passwd *pw, const char *type, + const unsigned char *buf, size_t size) +{ + unsigned char md5buf[MD5_DIGEST_LENGTH]; + /* + * Will contain the fingerprint MD5 in colonhex format. Need 3 bytes + * per MD5 byte: two for hex digits, plus one for either ':' or '\0'. + */ + char hex[MD5_DIGEST_LENGTH * 3]; + + md5_calc(md5buf, buf, size); + if (tohexstr(md5buf, sizeof (md5buf), hex, sizeof (hex)) != 0) + return (0); + return (sshd_allowed_in_capi(pw, hex)); +} + +/* ARGSUSED */ +int +sshd_user_rsa_key_allowed(struct passwd *pw, RSA *key, const char *fp) +{ + return (sshd_allowed_in_capi(pw, fp)); +} + +/* ARGSUSED */ +int +sshd_user_dsa_key_allowed(struct passwd *pw, DSA *key, const char *fp) +{ + return (sshd_allowed_in_capi(pw, fp)); +} + +/* ARGSUSED */ +int +sshd_user_ecdsa_key_allowed(struct passwd *pw, DSA *key, const char *fp) +{ + return (sshd_allowed_in_capi(pw, fp)); +} + +#ifdef __cplusplus +} +#endif diff --git a/usr/src/lib/libsmartsshd/i386/Makefile b/usr/src/lib/libsmartsshd/i386/Makefile new file mode 100644 index 0000000000..2bfe8001d9 --- /dev/null +++ b/usr/src/lib/libsmartsshd/i386/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libsmedia/library/Makefile.com b/usr/src/lib/libsmedia/library/Makefile.com index 1dabc5ae8d..cdaf3d7624 100644 --- a/usr/src/lib/libsmedia/library/Makefile.com +++ b/usr/src/lib/libsmedia/library/Makefile.com @@ -22,6 +22,8 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2018 Joyent, Inc. +# LIBRARY= libsmedia.a VERS=.1 diff --git a/usr/src/lib/libumem/amd64/umem_genasm.c b/usr/src/lib/libumem/amd64/umem_genasm.c index 39c3a14832..c5bc6048eb 100644 --- a/usr/src/lib/libumem/amd64/umem_genasm.c +++ b/usr/src/lib/libumem/amd64/umem_genasm.c @@ -75,7 +75,6 @@ #include <errno.h> -#include <stdio.h> const int umem_genasm_supported = 1; static uintptr_t umem_genasm_mptr = (uintptr_t)&_malloc; diff --git a/usr/src/lib/libvnd/Makefile b/usr/src/lib/libvnd/Makefile new file mode 100644 index 0000000000..1352adb8e4 --- /dev/null +++ b/usr/src/lib/libvnd/Makefile @@ -0,0 +1,50 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.lib + +HDRS = libvnd.h +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +TYPECHECK_LIB = libvnd.so.1 +TYPELIST = \ + vnd_ioc_attach_t \ + vnd_ioc_link_t \ + vnd_ioc_unlink_t \ + vnd_ioc_buf_t \ + vnd_ioc_info_t + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) $(TYPECHECK) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libvnd/Makefile.com b/usr/src/lib/libvnd/Makefile.com new file mode 100644 index 0000000000..c567a9b942 --- /dev/null +++ b/usr/src/lib/libvnd/Makefile.com @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2019 Joyent, Inc. +# + +include ../../Makefile.lib + +LIBRARY = libvnd.a +VERS = .1 +OBJECTS = libvnd.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc +CPPFLAGS += -I../common + +# false positives in vnd_create() +SMOFF += free + +SRCDIR = ../common + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libvnd/amd64/Makefile b/usr/src/lib/libvnd/amd64/Makefile new file mode 100644 index 0000000000..15d904c616 --- /dev/null +++ b/usr/src/lib/libvnd/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libvnd/common/libvnd.c b/usr/src/lib/libvnd/common/libvnd.c new file mode 100644 index 0000000000..8972f6cf5a --- /dev/null +++ b/usr/src/lib/libvnd/common/libvnd.c @@ -0,0 +1,550 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <stropts.h> +#include <stdio.h> +#include <zone.h> +#include <assert.h> +#include <sys/sysmacros.h> + +#include <sys/vnd.h> +#include <libvnd.h> + +struct vnd_handle { + int vh_fd; + uint32_t vh_errno; + int vh_syserr; +}; + +static const char *vnd_strerror_tbl[] = { + "no error", /* VND_E_SUCCESS */ + "not enough memory available", /* VND_E_NOMEM */ + "no such datalink", /* VND_E_NODATALINK */ + "datalink not of type DL_ETHER", /* VND_E_NOTETHER */ + "unknown dlpi failure", /* VND_E_DLPIINVAL */ + "DL_ATTACH_REQ failed", /* VND_E_ATTACHFAIL */ + "DL_BIND_REQ failed", /* VND_E_PROMISCFAIL */ + "DL_PROMISCON_REQ failed", /* VND_E_PROMISCFAIL */ + "DLD_CAPAB_DIRECT enable failed", /* VND_E_DIRECTFAIL */ + "bad datalink capability", /* VND_E_CAPACKINVAL */ + "bad datalink subcapability", /* VND_E_SUBCAPINVAL */ + "bad dld version", /* VND_E_DLDBADVERS */ + "failed to create kstats", /* VND_E_KSTATCREATE */ + "no such vnd link", /* VND_E_NODEV */ + "netstack doesn't exist", /* VND_E_NONETSTACK */ + "device already associated", /* VND_E_ASSOCIATED */ + "device already attached", /* VND_E_ATTACHED */ + "device already linked", /* VND_E_LINKED */ + "invalid name", /* VND_E_BADNAME */ + "permission denied", /* VND_E_PERM */ + "no such zone", /* VND_E_NOZONE */ + "failed to initialize vnd stream module", /* VND_E_STRINIT */ + "device not attached", /* VND_E_NOTATTACHED */ + "device not linked", /* VND_E_NOTLINKED */ + "another device has the same link name", /* VND_E_LINKEXISTS */ + "failed to create minor node", /* VND_E_MINORNODE */ + "requested buffer size is too large", /* VND_E_BUFTOOBIG */ + "requested buffer size is too small", /* VND_E_TOOSMALL */ + "unable to obtain exclusive access to dlpi link, link busy", + /* VND_E_DLEXCL */ + "DLD direct capability not supported over data link", + /* VND_E_DIRECTNOTSUP */ + "invalid property size", /* VND_E_BADPROPSIZE */ + "invalid property", /* VND_E_BADPROP */ + "property is read only", /* VND_E_PROPRDONLY */ + "unexpected system error", /* VND_E_SYS */ + "capabilities invalid, pass-through module detected", + /* VND_E_CAPABPASS */ + "unknown error" /* VND_E_UNKNOWN */ +}; + +vnd_errno_t +vnd_errno(vnd_handle_t *vhp) +{ + return (vhp->vh_errno); +} + +const char * +vnd_strerror(vnd_errno_t err) +{ + if (err >= VND_E_UNKNOWN) + err = VND_E_UNKNOWN; + return (vnd_strerror_tbl[err]); +} + +int +vnd_syserrno(vnd_handle_t *vhp) +{ + return (vhp->vh_syserr); +} + +const char * +vnd_strsyserror(int err) +{ + return (strerror(err)); +} + +static int +vnd_ioc_return(vnd_handle_t *vhp, uint32_t err) +{ + if (err != VND_E_SUCCESS) { + vhp->vh_errno = err; + vhp->vh_syserr = 0; + } else { + if (errno == EFAULT) + abort(); + vhp->vh_errno = VND_E_SYS; + vhp->vh_syserr = errno; + } + return (-1); +} + +void +vnd_close(vnd_handle_t *vhp) +{ + int ret; + + if (vhp->vh_fd >= 0) { + ret = close(vhp->vh_fd); + assert(ret == 0); + } + free(vhp); +} + +static int +vnd_link(vnd_handle_t *vhp, const char *name) +{ + vnd_ioc_link_t vil; + + if (strlen(name) >= VND_NAMELEN) { + errno = ENAMETOOLONG; + return (-1); + } + + (void) strlcpy(vil.vil_name, name, sizeof (vil.vil_name)); + vil.vil_errno = VND_E_SUCCESS; + if (ioctl(vhp->vh_fd, VND_IOC_LINK, &vil) != 0) + return (vnd_ioc_return(vhp, vil.vil_errno)); + + return (0); +} + +static vnd_handle_t * +vnd_open_ctl(vnd_errno_t *vnderr, int *syserr) +{ + int fd; + vnd_handle_t *vhp; + + vhp = malloc(sizeof (vnd_handle_t)); + if (vhp == NULL) { + if (vnderr != NULL) + *vnderr = VND_E_NOMEM; + if (syserr != NULL) + *syserr = 0; + return (NULL); + } + bzero(vhp, sizeof (vnd_handle_t)); + + fd = open("/dev/vnd/ctl", O_RDWR); + if (fd < 0) { + if (vnderr != NULL) + *vnderr = VND_E_SYS; + if (syserr != NULL) + *syserr = errno; + free(vhp); + return (NULL); + } + + vhp->vh_fd = fd; + return (vhp); +} + +vnd_handle_t * +vnd_create(const char *zonename, const char *datalink, const char *linkname, + vnd_errno_t *vnderr, int *syserr) +{ + int ret; + vnd_handle_t *vhp; + vnd_ioc_attach_t via; + zoneid_t zid; + + if (strlen(datalink) >= VND_NAMELEN) { + if (vnderr != NULL) + *vnderr = VND_E_BADNAME; + if (syserr != NULL) + *syserr = 0; + return (NULL); + } + + vhp = vnd_open_ctl(vnderr, syserr); + if (vhp == NULL) + return (NULL); /* errno set for us */ + + if (zonename != NULL) { + zid = getzoneidbyname(zonename); + if (zid == -1) { + vnd_close(vhp); + if (vnderr != NULL) + *vnderr = VND_E_NOZONE; + if (syserr != NULL) + *syserr = 0; + return (NULL); + } + via.via_zoneid = zid; + } else { + via.via_zoneid = -1; + } + + (void) strlcpy(via.via_name, datalink, sizeof (via.via_name)); + via.via_errno = VND_E_SUCCESS; + if (ioctl(vhp->vh_fd, VND_IOC_ATTACH, &via) != 0) { + if (via.via_errno != VND_E_SUCCESS) { + if (vnderr != NULL) + *vnderr = via.via_errno; + if (syserr != NULL) + *syserr = 0; + } else { + if (vnderr != NULL) + *vnderr = VND_E_SYS; + if (syserr != NULL) + *syserr = errno; + } + vnd_close(vhp); + return (NULL); + } + + ret = vnd_link(vhp, linkname); + if (ret != 0) { + if (vnderr != NULL) + *vnderr = vhp->vh_errno; + if (syserr != NULL) + *syserr = vhp->vh_syserr; + vnd_close(vhp); + return (NULL); + } + + if (vnderr != NULL) + *vnderr = VND_E_SUCCESS; + if (syserr != NULL) + *syserr = 0; + + return (vhp); +} + +vnd_handle_t * +vnd_open(const char *zone, const char *link, vnd_errno_t *vnderr, int *syserr) +{ + int fd, ret; + char path[MAXPATHLEN]; + vnd_handle_t *vhp; + + if (zone != NULL) + ret = snprintf(path, sizeof (path), "/dev/vnd/zone/%s/%s", + zone, link); + else + ret = snprintf(path, sizeof (path), "/dev/vnd/%s", link); + + if (ret >= sizeof (path)) { + if (vnderr != NULL) + *vnderr = VND_E_BADNAME; + if (syserr != NULL) + *syserr = 0; + return (NULL); + } + + fd = open(path, O_RDWR); + if (fd < 0) { + if (vnderr != NULL) + *vnderr = VND_E_SYS; + if (syserr != NULL) + *syserr = errno; + return (NULL); + } + + vhp = malloc(sizeof (vnd_handle_t)); + if (vhp == NULL) { + if (vnderr != NULL) + *vnderr = VND_E_NOMEM; + if (syserr != NULL) + *syserr = 0; + ret = close(fd); + assert(ret == 0); + return (NULL); + } + + bzero(vhp, sizeof (vnd_handle_t)); + vhp->vh_fd = fd; + + return (vhp); +} + +int +vnd_unlink(vnd_handle_t *vhp) +{ + vnd_ioc_unlink_t viu; + viu.viu_errno = VND_E_SUCCESS; + + if (ioctl(vhp->vh_fd, VND_IOC_UNLINK, &viu) != 0) + return (vnd_ioc_return(vhp, viu.viu_errno)); + + return (0); +} + +int +vnd_pollfd(vnd_handle_t *vhp) +{ + return (vhp->vh_fd); +} + +int +vnd_walk(vnd_walk_cb_t func, void *arg, vnd_errno_t *vnderr, int *syserr) +{ + vnd_handle_t *vhp; + vnd_ioc_list_t vl; + vnd_ioc_info_t *viip; + int i, ret; + + vl.vl_nents = 0; + vl.vl_ents = NULL; + + vhp = vnd_open_ctl(vnderr, syserr); + if (vhp == NULL) + return (-1); /* errno is set for us */ + + /* VND_IOC_LIST only returns generic errnos */ + if (ioctl(vhp->vh_fd, VND_IOC_LIST, &vl) != 0) { + if (vnderr != NULL) + *vnderr = VND_E_SYS; + if (syserr != NULL) + *syserr = errno; + (void) vnd_ioc_return(vhp, VND_E_SUCCESS); + vnd_close(vhp); + + return (-1); + } + + if (vl.vl_actents == 0) { + vnd_close(vhp); + return (0); + } + + viip = malloc(sizeof (vnd_ioc_info_t) * vl.vl_actents); + if (viip == NULL) { + if (vnderr != NULL) + *vnderr = VND_E_NOMEM; + if (syserr != NULL) + *syserr = 0; + vnd_close(vhp); + return (-1); + } + + vl.vl_nents = vl.vl_actents; + vl.vl_ents = viip; + + if (ioctl(vhp->vh_fd, VND_IOC_LIST, &vl) != 0) { + if (vnderr != NULL) + *vnderr = VND_E_SYS; + if (syserr != NULL) + *syserr = errno; + (void) vnd_ioc_return(vhp, VND_E_SUCCESS); + free(viip); + vnd_close(vhp); + return (-1); + } + + ret = 0; + for (i = 0; i < MIN(vl.vl_nents, vl.vl_actents); i++) { + if (func((vnd_info_t *)(viip + i), arg) != 0) { + ret = 1; + break; + } + } + + free(viip); + vnd_close(vhp); + + return (ret); +} + +static int +vnd_prop_readonly(vnd_handle_t *vhp) +{ + vhp->vh_syserr = 0; + vhp->vh_errno = VND_E_PROPRDONLY; + return (-1); +} + +/*ARGSUSED*/ +static int +vnd_prop_getbuf(vnd_handle_t *vhp, int cmd, void *buf, size_t len) +{ + vnd_ioc_buf_t vib; + vnd_prop_buf_t *vpbp = (vnd_prop_buf_t *)buf; + vib.vib_errno = 0; + + if (ioctl(vhp->vh_fd, cmd, &vib) != 0) + return (vnd_ioc_return(vhp, vib.vib_errno)); + + vpbp->vpb_size = vib.vib_size; + return (0); +} + +/*ARGSUSED*/ +static int +vnd_prop_setbuf(vnd_handle_t *vhp, int cmd, void *buf, size_t len) +{ + vnd_ioc_buf_t vib; + vnd_prop_buf_t *vpbp = (vnd_prop_buf_t *)buf; + + vib.vib_errno = 0; + vib.vib_size = vpbp->vpb_size; + if (ioctl(vhp->vh_fd, cmd, &vib) != 0) + return (vnd_ioc_return(vhp, vib.vib_errno)); + + return (0); +} + +typedef int (*vpt_prop_f)(vnd_handle_t *, int, void *, size_t); +typedef struct vnd_prop_tab { + vnd_prop_t vpt_prop; + size_t vpt_size; + int vpt_ioctl_get; + int vpt_ioctl_set; + vpt_prop_f vpt_get; + vpt_prop_f vpt_set; +} vnd_prop_tab_t; + +static vnd_prop_tab_t vnd_props[] = { + { VND_PROP_RXBUF, sizeof (vnd_prop_buf_t), VND_IOC_GETRXBUF, + VND_IOC_SETRXBUF, vnd_prop_getbuf, vnd_prop_setbuf}, + { VND_PROP_TXBUF, sizeof (vnd_prop_buf_t), VND_IOC_GETTXBUF, + VND_IOC_SETTXBUF, vnd_prop_getbuf, vnd_prop_setbuf }, + { VND_PROP_MAXBUF, sizeof (vnd_prop_buf_t), VND_IOC_GETMAXBUF, + -1, vnd_prop_getbuf, NULL }, + { VND_PROP_MINTU, sizeof (vnd_prop_buf_t), VND_IOC_GETMINTU, + -1, vnd_prop_getbuf, NULL }, + { VND_PROP_MAXTU, sizeof (vnd_prop_buf_t), VND_IOC_GETMAXTU, + -1, vnd_prop_getbuf, NULL }, + { VND_PROP_MAX } +}; + +static int +vnd_prop(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len, + boolean_t get) +{ + vnd_prop_tab_t *vpt; + + for (vpt = vnd_props; vpt->vpt_prop != VND_PROP_MAX; vpt++) { + if (vpt->vpt_prop != prop) + continue; + + if (len != vpt->vpt_size) { + vhp->vh_errno = VND_E_BADPROPSIZE; + vhp->vh_syserr = 0; + return (-1); + } + + if (get == B_TRUE) { + return (vpt->vpt_get(vhp, vpt->vpt_ioctl_get, buf, + len)); + } else { + if (vpt->vpt_set == NULL) + return (vnd_prop_readonly(vhp)); + return (vpt->vpt_set(vhp, vpt->vpt_ioctl_set, buf, + len)); + } + } + + vhp->vh_errno = VND_E_BADPROP; + vhp->vh_syserr = 0; + return (-1); +} + +int +vnd_prop_get(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len) +{ + return (vnd_prop(vhp, prop, buf, len, B_TRUE)); +} + +int +vnd_prop_set(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len) +{ + return (vnd_prop(vhp, prop, buf, len, B_FALSE)); +} + +int +vnd_prop_writeable(vnd_prop_t prop, boolean_t *write) +{ + vnd_prop_tab_t *vpt; + + for (vpt = vnd_props; vpt->vpt_prop != VND_PROP_MAX; vpt++) { + if (vpt->vpt_prop != prop) + continue; + + *write = (vpt->vpt_set != NULL); + return (0); + } + + return (-1); +} + +int +vnd_prop_iter(vnd_handle_t *vhp, vnd_prop_iter_f func, void *arg) +{ + int i; + + for (i = 0; i < VND_PROP_MAX; i++) { + if (func(vhp, i, arg) != 0) + return (1); + } + + return (0); +} + +int +vnd_frameio_read(vnd_handle_t *vhp, frameio_t *fiop) +{ + int ret; + + ret = ioctl(vhp->vh_fd, VND_IOC_FRAMEIO_READ, fiop); + if (ret == -1) { + vhp->vh_errno = VND_E_SYS; + vhp->vh_syserr = errno; + } + + return (ret); +} + +int +vnd_frameio_write(vnd_handle_t *vhp, frameio_t *fiop) +{ + int ret; + + ret = ioctl(vhp->vh_fd, VND_IOC_FRAMEIO_WRITE, fiop); + if (ret == -1) { + vhp->vh_errno = VND_E_SYS; + vhp->vh_syserr = errno; + } + + return (ret); +} diff --git a/usr/src/lib/libvnd/common/libvnd.h b/usr/src/lib/libvnd/common/libvnd.h new file mode 100644 index 0000000000..ea92f113b6 --- /dev/null +++ b/usr/src/lib/libvnd/common/libvnd.h @@ -0,0 +1,84 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#ifndef _LIBVND_H +#define _LIBVND_H + +/* + * libvnd interfaces + */ + +#include <stdint.h> +#include <sys/vnd_errno.h> +#include <sys/frameio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBVND_NAMELEN 32 + +typedef struct vnd_handle vnd_handle_t; + +extern vnd_handle_t *vnd_create(const char *, const char *, const char *, + vnd_errno_t *, int *); +extern vnd_handle_t *vnd_open(const char *, const char *, vnd_errno_t *, int *); +extern int vnd_unlink(vnd_handle_t *); +extern void vnd_close(vnd_handle_t *); +extern vnd_errno_t vnd_errno(vnd_handle_t *); +extern int vnd_syserrno(vnd_handle_t *); +extern const char *vnd_strerror(vnd_errno_t); +extern const char *vnd_strsyserror(int); + +extern int vnd_pollfd(vnd_handle_t *); + +typedef struct vnd_info { + uint32_t vi_version; + zoneid_t vi_zone; + char vi_name[LIBVND_NAMELEN]; + char vi_datalink[LIBVND_NAMELEN]; +} vnd_info_t; + +typedef int (*vnd_walk_cb_t)(vnd_info_t *, void *); +extern int vnd_walk(vnd_walk_cb_t, void *, vnd_errno_t *, int *); + +typedef enum vnd_prop { + VND_PROP_RXBUF = 0, + VND_PROP_TXBUF, + VND_PROP_MAXBUF, + VND_PROP_MINTU, + VND_PROP_MAXTU, + VND_PROP_MAX +} vnd_prop_t; + +typedef struct vnd_prop_buf { + uint64_t vpb_size; +} vnd_prop_buf_t; + +extern int vnd_prop_get(vnd_handle_t *, vnd_prop_t, void *, size_t); +extern int vnd_prop_set(vnd_handle_t *, vnd_prop_t, void *, size_t); +extern int vnd_prop_writeable(vnd_prop_t, boolean_t *); + +typedef int (*vnd_prop_iter_f)(vnd_handle_t *, vnd_prop_t, void *); +extern int vnd_prop_iter(vnd_handle_t *, vnd_prop_iter_f, void *); + +extern int vnd_frameio_read(vnd_handle_t *, frameio_t *); +extern int vnd_frameio_write(vnd_handle_t *, frameio_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVND_H */ diff --git a/usr/src/lib/libvnd/common/llib-lvnd b/usr/src/lib/libvnd/common/llib-lvnd new file mode 100644 index 0000000000..80a4229e32 --- /dev/null +++ b/usr/src/lib/libvnd/common/llib-lvnd @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <libvnd.h> diff --git a/usr/src/lib/libvnd/common/mapfile-vers b/usr/src/lib/libvnd/common/mapfile-vers new file mode 100644 index 0000000000..0eb862ab60 --- /dev/null +++ b/usr/src/lib/libvnd/common/mapfile-vers @@ -0,0 +1,55 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +# +# TODO When this makes it into illumos we should make it a public interface +# +SYMBOL_VERSION ILLUMOSprivate { + global: + vnd_create; + vnd_close; + vnd_errno; + vnd_frameio_read; + vnd_frameio_write; + vnd_open; + vnd_pollfd; + vnd_prop_get; + vnd_prop_iter; + vnd_prop_set; + vnd_prop_writeable; + vnd_strerror; + vnd_strsyserror; + vnd_syserrno; + vnd_unlink; + vnd_walk; + local: + *; +}; diff --git a/usr/src/lib/libvnd/i386/Makefile b/usr/src/lib/libvnd/i386/Makefile new file mode 100644 index 0000000000..41e699e8f8 --- /dev/null +++ b/usr/src/lib/libvnd/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libzdoor/Makefile b/usr/src/lib/libzdoor/Makefile new file mode 100644 index 0000000000..50d9545ef1 --- /dev/null +++ b/usr/src/lib/libzdoor/Makefile @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.lib + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +_msg: + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libzdoor/Makefile.com b/usr/src/lib/libzdoor/Makefile.com new file mode 100644 index 0000000000..09bde0f1b4 --- /dev/null +++ b/usr/src/lib/libzdoor/Makefile.com @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# + +LIBRARY= libzdoor.a +VERS= .1 +OBJECTS= zdoor.o \ + zdoor-int.o \ + ztree.o \ + zerror.o + +include ../../Makefile.lib +include ../../Makefile.rootfs + +SRCDIR = ../common +SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c) + +CPPFLAGS += -I$(SRCDIR) -D_REENTRANT -D_FILE_OFFSET_BITS=64 +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lzonecfg -lcontract + +$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libzdoor/amd64/Makefile b/usr/src/lib/libzdoor/amd64/Makefile new file mode 100644 index 0000000000..31be0ef7e6 --- /dev/null +++ b/usr/src/lib/libzdoor/amd64/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libzdoor/common/llib-lzdoor b/usr/src/lib/libzdoor/common/llib-lzdoor new file mode 100644 index 0000000000..a21a904b11 --- /dev/null +++ b/usr/src/lib/libzdoor/common/llib-lzdoor @@ -0,0 +1,34 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/*LINTLIBRARY*/ +/*PROTOLIB1*/ +/* + * + * Copyright 2011 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <zdoor.h> diff --git a/usr/src/lib/libzdoor/common/mapfile-vers b/usr/src/lib/libzdoor/common/mapfile-vers new file mode 100644 index 0000000000..38eba57ee2 --- /dev/null +++ b/usr/src/lib/libzdoor/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011, Joyent Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate_1.1 { + global: + zdoor_handle_init; + zdoor_handle_destroy; + zdoor_open; + zdoor_close; + local: + *; +}; diff --git a/usr/src/lib/libzdoor/common/zdoor-int.c b/usr/src/lib/libzdoor/common/zdoor-int.c new file mode 100644 index 0000000000..f77c1453d4 --- /dev/null +++ b/usr/src/lib/libzdoor/common/zdoor-int.c @@ -0,0 +1,324 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2012 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <errno.h> +#include <fcntl.h> +#include <sys/fork.h> +#include <libcontract.h> +#include <libzonecfg.h> +#include <sys/contract/process.h> +#include <sys/ctfs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "zdoor-int.h" +#include "zerror.h" + +#define ZDOOR_FMT_STR "/var/tmp/.%s" + + +static int +init_template(void) +{ + int fd = 0; + int err = 0; + + fd = open64(CTFS_ROOT "/process/template", O_RDWR); + if (fd == -1) + return (-1); + + err |= ct_tmpl_set_critical(fd, 0); + err |= ct_tmpl_set_informative(fd, 0); + err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); + err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); + if (err || ct_tmpl_activate(fd)) { + (void) close(fd); + return (-1); + } + + return (fd); +} + +static int +contract_latest(ctid_t *id) +{ + int cfd = 0; + int r = 0; + ct_stathdl_t st = {0}; + ctid_t result = {0}; + + if ((cfd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1) + return (errno); + if ((r = ct_status_read(cfd, CTD_COMMON, &st)) != 0) { + (void) close(cfd); + return (r); + } + + result = ct_status_get_id(st); + ct_status_free(st); + (void) close(cfd); + + *id = result; + return (0); +} + +static int +close_on_exec(int fd) +{ + int flags = fcntl(fd, F_GETFD, 0); + if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1)) + return (0); + return (-1); +} + +static int +contract_open(ctid_t ctid, const char *type, const char *file, int oflag) +{ + char path[PATH_MAX]; + int n = 0; + int fd = 0; + + if (type == NULL) + type = "all"; + + n = snprintf(path, PATH_MAX, CTFS_ROOT "/%s/%ld/%s", type, ctid, file); + if (n >= sizeof (path)) { + errno = ENAMETOOLONG; + return (-1); + } + + fd = open64(path, oflag); + if (fd != -1) { + if (close_on_exec(fd) == -1) { + int err = errno; + (void) close(fd); + errno = err; + return (-1); + } + } + return (fd); +} + +static int +contract_abandon_id(ctid_t ctid) +{ + int fd = 0; + int err = 0; + + fd = contract_open(ctid, "all", "ctl", O_WRONLY); + if (fd == -1) + return (errno); + + err = ct_ctl_abandon(fd); + (void) close(fd); + + return (err); +} + +/* + * zdoor_fattach(zone,service,door,detach_only) is heavily borrowed from + * zonestatd. Basically this forks, zone_enter's the targeted zone, + * fattaches to /var/tmp/.<service> with the door you've opened. + * detach_only gets passed in on door_stop to fdetach in the targeted zone. + * Note that this code really does require all the contract calls, which are + * all the static functions preceding this (have a look at zone_enter; without + * that code zone_enter will kick back EINVAL). + */ +int +zdoor_fattach(zoneid_t zoneid, const char *service, int door, int detach_only) +{ + int fd = 0; + int len = 0; + int pid = 0; + int stat = 0; + int tmpl_fd = 0; + char path[MAXPATHLEN] = {0}; + ctid_t ct = -1; + + if (zoneid < 0) { + zdoor_debug("zdoor_fattach: zoneid < 0"); + return (ZDOOR_ARGS_ERROR); + } + + if (service == NULL) { + zdoor_debug("zdoor_fattach: NULL service"); + return (ZDOOR_ARGS_ERROR); + } + + if ((tmpl_fd = init_template()) < 0) { + zdoor_warn("zdoor_fattach: init contract for %d:%s failed", + zoneid, service); + return (ZDOOR_ERROR); + } + + len = snprintf(NULL, 0, ZDOOR_FMT_STR, service) + 1; + if (len > MAXPATHLEN) + return (ZDOOR_ARGS_ERROR); + (void) snprintf(path, len, ZDOOR_FMT_STR, service); + + zdoor_info("zdoor_fattach: ensuring %s", path); + + pid = fork(); + if (pid < 0) { + (void) ct_tmpl_clear(tmpl_fd); + zdoor_error("zdoor_fattach: unable to fork for zone_enter: %s", + strerror(errno)); + return (ZDOOR_OK); + } + + if (pid == 0) { + zdoor_debug("zdoor_fattach(CHILD): starting"); + (void) ct_tmpl_clear(tmpl_fd); + (void) close(tmpl_fd); + if (zone_enter(zoneid) != 0) { + zdoor_debug("zdoor_fattach(CHILD): zone_enter fail %s", + strerror(errno)); + if (errno == EINVAL) { + _exit(0); + } + _exit(1); + } + (void) fdetach(path); + (void) unlink(path); + if (detach_only) { + zdoor_debug("zdoor_fattach(CHILD): detach only, done"); + _exit(0); + } + fd = open(path, O_CREAT|O_RDWR, 0644); + if (fd < 0) { + zdoor_debug("zdoor_fattach(CHILD): open failed: %s", + strerror(errno)); + _exit(2); + } + if (fattach(door, path) != 0) { + zdoor_debug("zdoor_fattach(CHILD): fattach failed: %s", + strerror(errno)); + _exit(3); + } + _exit(0); + } + if (contract_latest(&ct) == -1) + ct = -1; + (void) ct_tmpl_clear(tmpl_fd); + (void) close(tmpl_fd); + (void) contract_abandon_id(ct); + + zdoor_debug("zdoor_fattach: waiting for child..."); + while (waitpid(pid, &stat, 0) != pid) + ; + if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) { + zdoor_debug(" child exited with success"); + zdoor_debug("zdoor_fattach: returning ZDOOR_OK"); + return (ZDOOR_OK); + } + + zdoor_debug(" child exited with %d", WEXITSTATUS(stat)); + zdoor_debug("zdoor_fattach: returning ZDOOR_ERROR"); + return (ZDOOR_ERROR); +} + +/* + * zdoor_zone_is_running(zone) returns 1 if the specified zone is running, or 0 + * if it is any other state. It additionally eats any other errors it + * encounters and returns 0 upon encountering them. + */ +boolean_t +zdoor_zone_is_running(zoneid_t zoneid) +{ + zone_state_t state; + char zone[ZONENAME_MAX]; + if (zoneid < 0) + return (B_FALSE); + + if (getzonenamebyid(zoneid, zone, ZONENAME_MAX) < 0) + return (B_FALSE); + + if (!zone_get_state((char *)zone, &state) == Z_OK) + return (B_FALSE); + + return (state == ZONE_STATE_RUNNING); +} + +/* + * zdoor_cookie_create simply allocates and initializes + * memory. Returns NULL on any error. + */ +zdoor_cookie_t * +zdoor_cookie_create(const char *zonename, const char *service, +const void *biscuit) +{ + zdoor_cookie_t *cookie = NULL; + + if (zonename == NULL || service == NULL) + return (NULL); + + cookie = (zdoor_cookie_t *)calloc(1, sizeof (zdoor_cookie_t)); + if (cookie == NULL) { + OUT_OF_MEMORY(); + return (NULL); + } + cookie->zdc_biscuit = (void *)biscuit; + cookie->zdc_zonename = strdup((char *)zonename); + if (cookie->zdc_zonename == NULL) { + zdoor_cookie_free(cookie); + OUT_OF_MEMORY(); + return (NULL); + } + cookie->zdc_service = strdup((char *)service); + if (cookie->zdc_service == NULL) { + zdoor_cookie_free(cookie); + OUT_OF_MEMORY(); + return (NULL); + } + + return (cookie); +} + +/* + * zdoor_cookie_free(cookie) cleans up any memory associated with the + * specified cookie. + */ +void +zdoor_cookie_free(zdoor_cookie_t *cookie) +{ + if (cookie == NULL) + return; + + if (cookie->zdc_zonename != NULL) { + free(cookie->zdc_zonename); + cookie->zdc_zonename = NULL; + } + + if (cookie->zdc_service != NULL) { + free(cookie->zdc_service); + cookie->zdc_service = NULL; + } + + free(cookie); +} diff --git a/usr/src/lib/libzdoor/common/zdoor-int.h b/usr/src/lib/libzdoor/common/zdoor-int.h new file mode 100644 index 0000000000..782452c426 --- /dev/null +++ b/usr/src/lib/libzdoor/common/zdoor-int.h @@ -0,0 +1,64 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2011 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ZDOOR_INT_H +#define _ZDOOR_INT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <pthread.h> +#include <zdoor.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum zdoor_action_t { + ZDOOR_ACTION_NOOP, + ZDOOR_ACTION_STOP, + ZDOOR_ACTION_START +} zdoor_action_t; + +struct zdoor_handle { + pthread_mutex_t zdh_lock; + void *zdh_zonecfg_handle; + void *zdh_ztree; +}; + +zdoor_cookie_t *zdoor_cookie_create(const char *zonename, const char *service, + const void *biscuit); + +void zdoor_cookie_free(zdoor_cookie_t *cookie); + +boolean_t zdoor_zone_is_running(zoneid_t zoneid); + +int zdoor_fattach(zoneid_t zoneid, const char *service, int door, + int detach_only); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZDOOR_INT_H */ diff --git a/usr/src/lib/libzdoor/common/zdoor.c b/usr/src/lib/libzdoor/common/zdoor.c new file mode 100644 index 0000000000..aa52458f90 --- /dev/null +++ b/usr/src/lib/libzdoor/common/zdoor.c @@ -0,0 +1,431 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <alloca.h> +#include <door.h> +#include <errno.h> +#include <fcntl.h> +#include <libzonecfg.h> +#include <pthread.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <syslog.h> +#include <stdlib.h> +#include <string.h> +#include <stropts.h> +#include <unistd.h> +#include <zdoor.h> +#include <zone.h> + +#include "zdoor-int.h" +#include "zerror.h" +#include "ztree.h" + +extern void * +zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid, + const char *newstate, const char *oldstate, hrtime_t when, + void *p), void *p); + +extern void +zonecfg_notify_unbind(void *handle); + +/* + * _callback(cookie, door_args...) is our private function that we tell + * the Solaris door API about. This function does some sanity checking on + * arguments and issues a callback to the owner of this door. That API + * will return us memory that needs to be sent back to the client on the + * other end of the door, but since the door_return API never gives you + * back control of the function, this does a simple alloca/memcpy and + * frees up the memory pointed to by the parent. While this really doesn't + * let a client do much other than pass a simple struct of primitives (or + * more likely more common a char *), that's the way the door API works, + * and so this isn't really imposing any restriction that didn't already + * need to be dealt with by someone. This is why the zdoor_result structure + * takes a char *, rather than a void * for the data pointer. + */ +static void +_callback(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, + uint_t n_desc) +{ + zdoor_result_t *result = NULL; + void *door_response = NULL; + int size = 0; + dtree_entry_t *entry = (dtree_entry_t *)cookie; + + if (entry == NULL) { + zdoor_warn("_callback: NULL cookie? door_returning"); + (void) door_return(NULL, 0, NULL, 0); + } + + (void) pthread_mutex_lock(&entry->dte_parent->zte_parent->zdh_lock); + zdoor_debug("_callback: calling back with %p", entry->dte_cookie); + result = entry->dte_callback(entry->dte_cookie, argp, arg_size); + zdoor_debug("_callback: app callback returned %p", result); + (void) pthread_mutex_unlock(&entry->dte_parent->zte_parent->zdh_lock); + + if (result == NULL) { + zdoor_debug("_callback: door_returning NULL"); + (void) door_return(NULL, 0, NULL, 0); + } + + if (result->zdr_data != NULL && result->zdr_size > 0) { + door_response = alloca(result->zdr_size); + if (door_response != NULL) { + size = result->zdr_size; + (void) memcpy(door_response, + (void *) result->zdr_data, size); + } + } + + if (result->zdr_data != NULL) + free(result->zdr_data); + free(result); + + zdoor_debug("_callback: door_returning %p, %d", door_response, size); + (void) door_return(door_response, size, NULL, 0); +} + +static void +zdoor_stop(dtree_entry_t *entry) +{ + zoneid_t zid = -1; + + if (entry == NULL) { + zdoor_debug("zdoor_stop: NULL arguments"); + return; + } + + zdoor_debug("zdoor_stop: entry=%p, zone=%s, service=%s", + entry, entry->dte_parent->zte_zonename, entry->dte_service); + + zid = getzoneidbyname(entry->dte_parent->zte_zonename); + (void) zdoor_fattach(zid, entry->dte_service, entry->dte_door, 1); + (void) door_revoke(entry->dte_door); + entry->dte_door = -1; + + zdoor_debug("zdoor_stop returning"); +} + +/* + * zdoor_create is called both by the main API + * call zdoor_open, as well as by the zone_monitor code upon a zone restart + * (assuming it already has a door in it). This code assumes that the + * permissions were correct (e.g., the target door is not a GZ, that this + * program is being run out of the GZ), but does not assume that the target + * door file has not changed out from under us, so that is explicitly rechecked. + * + * This also assumes the parent has already locked handle. + */ +static int +zdoor_create(dtree_entry_t *entry) +{ + int status = ZDOOR_OK; + zoneid_t zid = -1; + + if (entry == NULL) { + zdoor_debug("zdoor_create: NULL arguments"); + return (ZDOOR_ARGS_ERROR); + } + + zdoor_debug("zdoor_create: entry=%p, zone=%s, service=%s", + entry, entry->dte_parent->zte_zonename, entry->dte_service); + + zid = getzoneidbyname(entry->dte_parent->zte_zonename); + if (zid < 0) { + zdoor_info("zdoor_create: %s is a non-existient zone", + entry->dte_parent->zte_zonename); + return (ZDOOR_ERROR); + } + if (!zdoor_zone_is_running(zid)) { + zdoor_debug("zdoor_create: %s is not running", + entry->dte_parent->zte_zonename); + return (ZDOOR_ZONE_NOT_RUNNING); + } + + entry->dte_door = door_create(_callback, entry, 0); + zdoor_info("zdoor_create: door_create returned %d", entry->dte_door); + if (entry->dte_door < 0) { + zdoor_stop(entry); + return (ZDOOR_ERROR); + } + + status = zdoor_fattach(zid, entry->dte_service, entry->dte_door, 0); + + zdoor_debug("zdoor_create: returning %d", status); + return (status); +} + + +/* + * door_visitor(entry) is a callback from the ztree code that checks whether + * or not we should be taking some action on a given door. Note that the + * callpath to this API is: + * SYSTEM -> + * zone_monitor -> + * ztree_walk -> + * door_visitor + * + * Which is important to note that this API assumes that all things needing + * locking are locked by a parent caller (which is the zone_monitor). + */ +static void +zdoor_visitor(dtree_entry_t *entry) +{ + if (entry == NULL) { + zdoor_info("zdoor_visitor: entered with NULL entry"); + return; + } + + zdoor_debug("zdoor_visitor: entered for entry=%p, service=%s", + entry, entry->dte_service); + + if (entry->dte_parent->zte_action == ZDOOR_ACTION_STOP) { + zdoor_debug(" stopping zdoor"); + zdoor_stop(entry); + } else if (entry->dte_parent->zte_action == ZDOOR_ACTION_START) { + zdoor_debug(" starting zdoor"); + if (zdoor_create(entry) != ZDOOR_OK) { + zdoor_error("door_visitor: Unable to restart zdoor\n"); + } + } +} + +/* + * zone_monitor(zonename, zid, newstate, oldstate, when, cookie) is our + * registered callback with libzonecfg to notify us of any changes to a + * given zone. This activates a walk on all doors for a zone iff the state + * is changing from running or into running. + */ +static int +zone_monitor(const char *zonename, zoneid_t zid, const char *newstate, + const char *oldstate, hrtime_t when, void *p) +{ + zdoor_handle_t handle = (zdoor_handle_t)p; + ztree_entry_t *entry = NULL; + + if (handle == NULL) { + zdoor_warn("zone_monitor: entered with NULL handle?"); + return (-1); + } + + zdoor_info("zone_monitor: zone=%s, zid=%d, newst=%s, oldst=%s, p=%p", + zonename, zid, newstate, oldstate, p); + + (void) pthread_mutex_lock(&(handle->zdh_lock)); + entry = ztree_zone_find(handle, zonename); + if (entry != NULL) { + zdoor_debug(" found entry in ztree"); + entry->zte_action = ZDOOR_ACTION_NOOP; + if (strcmp("running", newstate) == 0) { + if (strcmp("ready", oldstate) == 0) + entry->zte_action = ZDOOR_ACTION_START; + } else if (strcmp("shutting_down", newstate) == 0) { + if (strcmp("running", oldstate) == 0) + entry->zte_action = ZDOOR_ACTION_STOP; + } + zdoor_debug(" set state to: %d", entry->zte_action); + if (entry->zte_action != ZDOOR_ACTION_NOOP) + ztree_walk_doors(handle, zonename); + } + (void) pthread_mutex_unlock(&(handle->zdh_lock)); + + zdoor_info("zone_monitor: returning"); + return (0); +} + +zdoor_handle_t +zdoor_handle_init() +{ + zdoor_handle_t handle = NULL; + + zdoor_debug("zdoor_handle_init entered"); + + handle = (zdoor_handle_t)calloc(1, sizeof (struct zdoor_handle)); + if (handle == NULL) { + OUT_OF_MEMORY(); + return (NULL); + } + + (void) pthread_mutex_init(&(handle->zdh_lock), NULL); + handle->zdh_zonecfg_handle = zonecfg_notify_bind(zone_monitor, handle); + if (handle->zdh_zonecfg_handle == NULL) { + zdoor_error("zonecfg_notify_bind failure: %s", strerror(errno)); + return (NULL); + } + + zdoor_debug("zdoor_handle_init returning %p", handle); + return (handle); +} + +void +zdoor_handle_destroy(zdoor_handle_t handle) +{ + if (handle == NULL) { + zdoor_debug("zdoor_handle_destroy: NULL arguments"); + return; + } + + zdoor_debug("zdoor_handle_destroy: handle=%p", handle); + + (void) pthread_mutex_lock(&(handle->zdh_lock)); + zonecfg_notify_unbind(handle->zdh_zonecfg_handle); + (void) pthread_mutex_unlock(&(handle->zdh_lock)); + (void) pthread_mutex_destroy(&(handle->zdh_lock)); + free(handle); +} + +/* + * zdoor_open(zone, service, biscuit, callback) is the main public facing API in + * libzdoor. It will open a door with the name .[service] under + * [zonepath]/root/var/tmp, where [zonepath] is resolved on the fly. Note this + * API can only be invoked from the global zone, and will not allow you to open + * a zdoor in the global zone. + */ +int +zdoor_open(zdoor_handle_t handle, const char *zonename, const char *service, + void *biscuit, zdoor_callback callback) +{ + zdoor_cookie_t *zdoor_cookie = NULL; + int rc = -1; + int status = ZDOOR_OK; + zoneid_t zid = -1; + dtree_entry_t *entry = NULL; + + if (handle == NULL || zonename == NULL || + service == NULL || callback == NULL) { + zdoor_debug("zdoor_open: NULL arguments"); + return (ZDOOR_ARGS_ERROR); + } + zdoor_debug("zdoor_open: entered: handle=%p, zone=%s, service=%s", + handle, zonename, service); + + if (getzoneid() != GLOBAL_ZONEID) { + zdoor_warn("zdoor_open: not invoked from global zone"); + return (ZDOOR_NOT_GLOBAL_ZONE); + } + + + zid = getzoneidbyname(zonename); + if (zid < 0) { + zdoor_info("zdoor_open: %s is a non-existent zone", zonename); + return (ZDOOR_ARGS_ERROR); + } + + if (zid == GLOBAL_ZONEID) { + zdoor_warn("zdoor_open: zdoors not allowed in global zone"); + return (ZDOOR_ZONE_FORBIDDEN); + } + + if (!zdoor_zone_is_running(zid)) { + zdoor_info("zdoor_open: %s is not running", zonename); + return (ZDOOR_ZONE_NOT_RUNNING); + } + + zdoor_cookie = zdoor_cookie_create(zonename, service, biscuit); + if (zdoor_cookie == NULL) { + OUT_OF_MEMORY(); + return (ZDOOR_OUT_OF_MEMORY); + } + + (void) pthread_mutex_lock(&(handle->zdh_lock)); + rc = ztree_zone_add(handle, zonename, zdoor_visitor); + if (rc != ZTREE_SUCCESS && rc != ZTREE_ALREADY_EXISTS) { + zdoor_debug("zdoor_open: unable to add zone to ztree: %d", rc); + status = ZDOOR_ERROR; + goto out; + } + rc = ztree_door_add(handle, zonename, service, callback, + zdoor_cookie); + if (rc != ZTREE_SUCCESS) { + zdoor_debug("zdoor_open: unable to add door to ztree: %d", rc); + if (rc == ZTREE_ALREADY_EXISTS) { + zdoor_warn("service %s already has a zdoor", service); + } + status = ZDOOR_ERROR; + goto out; + } + + entry = ztree_door_find(handle, zonename, service); + if (entry == NULL) { + zdoor_debug("zdoor_open: unable to find door in ztree?"); + status = ZDOOR_ERROR; + goto out; + } + if (zdoor_create(entry) != ZDOOR_OK) { + zdoor_info("zdoor_open: zdoor_create failed."); + status = ZDOOR_ERROR; + goto out; + } +out: + if (status != ZDOOR_OK) { + zdoor_debug("zdoor_open: status not ok, stopping and cleaning"); + zdoor_stop(entry); + (void) ztree_door_remove(handle, entry); + zdoor_cookie_free(zdoor_cookie); + } + (void) pthread_mutex_unlock(&(handle->zdh_lock)); + zdoor_debug("zdoor_open: returning %d", status); + return (status); +} + +/* + * zdoor_close(zone, service) unregisters a previously created zdoor, and + * returns the biscuit provided at creation time, so the caller can free it. + * Returns NULL on any error. + */ +void * +zdoor_close(zdoor_handle_t handle, const char *zonename, const char *service) +{ + dtree_entry_t *entry = NULL; + zdoor_cookie_t *cookie = NULL; + void *biscuit = NULL; + + if (handle == NULL || zonename == NULL || service == NULL) { + zdoor_debug("zdoor_close: NULL arguments"); + return (NULL); + } + + zdoor_debug("zdoor_close: entered handle=%p, zone=%s, service=%s", + handle, zonename, service); + + (void) pthread_mutex_lock(&(handle->zdh_lock)); + + entry = ztree_door_find(handle, zonename, service); + if (entry != NULL) { + zdoor_debug("zdoor_close: found door in ztree, stopping"); + zdoor_stop(entry); + cookie = ztree_door_remove(handle, entry); + if (cookie != NULL) { + biscuit = cookie->zdc_biscuit; + zdoor_cookie_free(cookie); + } + } else { + zdoor_debug("zdoor_close: didn't find door in ztree"); + } + + (void) pthread_mutex_unlock(&(handle->zdh_lock)); + + zdoor_debug("zdoor_close: returning %p", biscuit); + return (biscuit); +} diff --git a/usr/src/lib/libzdoor/common/zerror.c b/usr/src/lib/libzdoor/common/zerror.c new file mode 100644 index 0000000000..5ccb449e1b --- /dev/null +++ b/usr/src/lib/libzdoor/common/zerror.c @@ -0,0 +1,117 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2011 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <pthread.h> +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "zerror.h" + +static const char *PREFIX = "%s ZDOOR:%s:T(%d): "; + +static const char *DEBUG_ENV_VAR = "ZDOOR_TRACE"; + +static boolean_t +is_debug_enabled() +{ + boolean_t enabled = B_FALSE; + const char *_envp = getenv(DEBUG_ENV_VAR); + if (_envp != NULL && atoi(_envp) >= 2) + enabled = B_TRUE; + + return (enabled); +} + +static boolean_t +is_info_enabled() +{ + boolean_t enabled = B_FALSE; + const char *_envp = getenv(DEBUG_ENV_VAR); + if (_envp != NULL && atoi(_envp) >= 1) + enabled = B_TRUE; + + return (enabled); +} + +void +zdoor_debug(const char *fmt, ...) +{ + va_list alist; + + if (!is_debug_enabled()) + return; + + va_start(alist, fmt); + + (void) fprintf(stderr, PREFIX, __TIME__, "DEBUG", pthread_self()); + (void) vfprintf(stderr, fmt, alist); + (void) fprintf(stderr, "\n"); + va_end(alist); +} + +void +zdoor_info(const char *fmt, ...) +{ + va_list alist; + + if (!is_info_enabled()) + return; + + va_start(alist, fmt); + + (void) fprintf(stderr, PREFIX, __TIME__, "INFO", pthread_self()); + (void) vfprintf(stderr, fmt, alist); + (void) fprintf(stderr, "\n"); + va_end(alist); +} + +void +zdoor_warn(const char *fmt, ...) +{ + va_list alist; + + va_start(alist, fmt); + + (void) fprintf(stderr, PREFIX, __TIME__, "WARN", pthread_self()); + (void) vfprintf(stderr, fmt, alist); + (void) fprintf(stderr, "\n"); + va_end(alist); +} + +void +zdoor_error(const char *fmt, ...) +{ + va_list alist; + + va_start(alist, fmt); + + (void) fprintf(stderr, PREFIX, __TIME__, "ERROR", pthread_self()); + (void) vfprintf(stderr, fmt, alist); + (void) fprintf(stderr, "\n"); + va_end(alist); +} diff --git a/usr/src/lib/libzdoor/common/zerror.h b/usr/src/lib/libzdoor/common/zerror.h new file mode 100644 index 0000000000..afc848fcea --- /dev/null +++ b/usr/src/lib/libzdoor/common/zerror.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2011 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ZERROR_H +#define _ZERROR_H + +#include <stdio.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern void zdoor_debug(const char *fmt, ...); +extern void zdoor_info(const char *fmt, ...); +extern void zdoor_warn(const char *fmt, ...); +extern void zdoor_error(const char *fmt, ...); + +#define OUT_OF_MEMORY() \ + zdoor_error("Out of Memory at %s:%d", __FILE__, __LINE__) + +#ifdef __cplusplus +} +#endif + +#endif /* _ZERROR_H */ diff --git a/usr/src/lib/libzdoor/common/ztree.c b/usr/src/lib/libzdoor/common/ztree.c new file mode 100644 index 0000000000..22ef0ad94f --- /dev/null +++ b/usr/src/lib/libzdoor/common/ztree.c @@ -0,0 +1,343 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <search.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "zerror.h" +#include "ztree.h" + + +/* + * ztree is just a helpful wrapper over a tsearch binary tree that deals with + * all of the libzdoor types. + * + * So what this ztree actually is is a tree of trees. The outer tree is a tree + * of zones, and each node holds a tree of doors. + */ + +/* + * _ztree_compare(p1, p2) is the tsearch callback for comparing the "outer" + * tree (e.g., the one of zones). + */ +static int +_ztree_compare(const void *p1, const void *p2) +{ + ztree_entry_t *z1 = (ztree_entry_t *)p1; + ztree_entry_t *z2 = (ztree_entry_t *)p2; + + if (z1 == NULL && z2 == NULL) + return (0); + if (z1 == NULL && z2 != NULL) + return (-1); + if (z1 != NULL && z2 == NULL) + return (1); + + return (strcmp(z1->zte_zonename, z2->zte_zonename)); +} + +/* + * _dtree_compare(p1, p2) is the tsearch callback for comparing the "inner" + * tree (e.g., the one of doors). + */ +static int +_dtree_compare(const void *p1, const void *p2) +{ + dtree_entry_t *d1 = (dtree_entry_t *)p1; + dtree_entry_t *d2 = (dtree_entry_t *)p2; + + if (d1 == NULL && d2 == NULL) + return (0); + if (d1 == NULL && d2 != NULL) + return (-1); + if (d1 != NULL && d2 == NULL) + return (1); + + return (strcmp(d1->dte_service, d2->dte_service)); +} + +static void +ztree_entry_free(ztree_entry_t *entry) +{ + if (entry == NULL) + return; + + if (entry->zte_zonename != NULL) + free(entry->zte_zonename); + + free(entry); +} + +static void +dtree_entry_free(dtree_entry_t *entry) +{ + if (entry == NULL) + return; + + if (entry->dte_service) + free(entry->dte_service); + + free(entry); +} + + +/* + * ztree_zone_add inserts a new zone into the tree iff + * there is not already an entry for that zone. This method returns one of + * four possible return codes, ZTREE_SUCCESS on :), ZTREE_ARGUMENT_ERROR if + * zone is NULL, ZTREE_ERROR if there is internal failure (e.g., OOM), and + * ZTREE_ALREADY_EXISTS if the zone is already in the tree. + */ +int +ztree_zone_add(struct zdoor_handle *handle, const char *zonename, + ztree_door_visitor visitor) +{ + ztree_entry_t *entry = NULL; + void *ret = NULL; + int status = ZTREE_SUCCESS; + + if (handle == NULL || zonename == NULL) + return (ZTREE_ARGUMENT_ERROR); + + entry = (ztree_entry_t *)calloc(1, sizeof (ztree_entry_t)); + if (entry == NULL) { + OUT_OF_MEMORY(); + return (ZTREE_ERROR); + } + entry->zte_zonename = strdup(zonename); + if (entry->zte_zonename == NULL) { + ztree_entry_free(entry); + OUT_OF_MEMORY(); + return (ZTREE_ERROR); + } + entry->zte_action = ZDOOR_ACTION_NOOP; + entry->zte_parent = handle; + entry->zte_visitor = visitor; + + ret = tsearch(entry, &(handle->zdh_ztree), _ztree_compare); + if (ret == NULL) { + ztree_entry_free(entry); + status = ZTREE_ERROR; + OUT_OF_MEMORY(); + } else if ((*(ztree_entry_t **)ret) != entry) { + ztree_entry_free(entry); + status = ZTREE_ALREADY_EXISTS; + } + + return (status); +} + + +/* + * ztree_zone_find returns the entry in the "outer" tree representing + * this zone, if it exists, NULL otherwise. + */ +ztree_entry_t * +ztree_zone_find(struct zdoor_handle *handle, const char *zonename) +{ + ztree_entry_t key = {0}; + void *ret = NULL; + + if (handle == NULL || zonename == NULL) + return (NULL); + + key.zte_zonename = (char *)zonename; + ret = tfind(&key, &(handle->zdh_ztree), _ztree_compare); + + return (ret != NULL ? *(ztree_entry_t **)ret : NULL); +} + + +/* + * ztree_zone_remove removes an entry from the "outer" zone iff the + * zone exists. The cookie set by the creator is returned. + */ +void +ztree_zone_remove(struct zdoor_handle *handle, ztree_entry_t *entry) +{ + if (handle == NULL || entry == NULL) + return; + + (void) tdelete(entry, &(handle->zdh_ztree), _ztree_compare); + ztree_entry_free(entry); +} + + +/* + * ztree_door_add inserts a new door into the inner tree iff + * there is not already an entry for that door. This method returns one of + * four possible return codes, ZTREE_SUCCESS on :), ZTREE_ARGUMENT_ERROR if + * zone is NULL, ZTREE_ERROR if there is internal failure (e.g., OOM), and + * ZTREE_ALREADY_EXISTS if the door is already in the tree. + */ +int +ztree_door_add(struct zdoor_handle *handle, const char *zonename, + const char *service, zdoor_callback callback, zdoor_cookie_t *cookie) +{ + dtree_entry_t *entry = NULL; + ztree_entry_t *znode = NULL; + void *ret = NULL; + int status = ZTREE_SUCCESS; + + if (handle == NULL || zonename == NULL || service == NULL) + return (ZTREE_ARGUMENT_ERROR); + + znode = ztree_zone_find(handle, zonename); + if (znode == NULL) + return (ZTREE_NOT_FOUND); + + entry = (dtree_entry_t *)calloc(1, sizeof (dtree_entry_t)); + if (entry == NULL) { + OUT_OF_MEMORY(); + return (ZTREE_ERROR); + } + entry->dte_parent = znode; + entry->dte_callback = callback; + entry->dte_cookie = cookie; + entry->dte_service = strdup(service); + if (entry->dte_service == NULL) { + free(entry); + OUT_OF_MEMORY(); + return (ZTREE_ERROR); + } + + ret = tsearch(entry, &(znode->zte_door_tree), _dtree_compare); + if (ret == NULL) { + dtree_entry_free(entry); + OUT_OF_MEMORY(); + status = ZTREE_ERROR; + } else if ((*(dtree_entry_t **)ret) != entry) { + dtree_entry_free(entry); + status = ZTREE_ALREADY_EXISTS; + } else { + znode->zte_num_doors++; + } + + return (status); +} + + +/* + * ztree_door_find returns the entry in the "inner" tree + * representing this zone, if it exists, NULL otherwise. + */ +dtree_entry_t * +ztree_door_find(struct zdoor_handle *handle, const char *zonename, + const char *service) +{ + dtree_entry_t key = {0}; + ztree_entry_t *znode = NULL; + void *ret = NULL; + + if (handle == NULL || zonename == NULL || service == NULL) + return (NULL); + + znode = ztree_zone_find(handle, zonename); + if (znode == NULL) + return (NULL); + + key.dte_service = (char *)service; + ret = tfind(&key, &(znode->zte_door_tree), _dtree_compare); + + return (ret != NULL ? *(dtree_entry_t **)ret : NULL); +} + + +/* + * ztree_door_remove(zone, door) removes a node from the inner tree iff + * both the door and zone exist. Note this frees the node as well. The + * cookie set by the creator is returned. + */ +zdoor_cookie_t * +ztree_door_remove(struct zdoor_handle *handle, dtree_entry_t *entry) +{ + zdoor_cookie_t *cookie = NULL; + ztree_entry_t *znode = NULL; + + if (handle == NULL || entry == NULL) + return (NULL); + + znode = entry->dte_parent; + cookie = entry->dte_cookie; + + (void) tdelete(entry, &(znode->zte_door_tree), _dtree_compare); + dtree_entry_free(entry); + + znode->zte_num_doors--; + if (znode->zte_num_doors == 0) { + zdoor_debug("ztree: zone %s has no doors left, removing", + znode->zte_zonename); + ztree_zone_remove(handle, znode); + } + + return (cookie); +} + + +/* + * _ztree_door_visitor(nodep, which, depth) is the private function we use + * to wrap up tsearch's goofy API. We're really just using this to ensure + * zdoor doesn't get called > 1 times for a given entity in the ztree. + */ +static void +_ztree_door_visitor(const void *nodep, const VISIT which, const int depth) +{ + dtree_entry_t *entry = *(dtree_entry_t **)nodep; + + if (entry == NULL) + return; + + switch (which) { + case preorder: + case endorder: + break; + case postorder: + case leaf: + if (entry->dte_parent->zte_visitor != NULL) + entry->dte_parent->zte_visitor(entry); + break; + } +} + + +/* + * ztree_walk_doors(zone) will proceed to visit every node in the "inner" tree + * for this zone, and callback the visitor that was registered on tree creation. + */ +void +ztree_walk_doors(struct zdoor_handle *handle, const char *zonename) +{ + ztree_entry_t *znode = NULL; + + if (handle == NULL || zonename == NULL) + return; + + znode = ztree_zone_find(handle, zonename); + if (znode == NULL) + return; + + twalk(znode->zte_door_tree, _ztree_door_visitor); +} diff --git a/usr/src/lib/libzdoor/common/ztree.h b/usr/src/lib/libzdoor/common/ztree.h new file mode 100644 index 0000000000..b46dace287 --- /dev/null +++ b/usr/src/lib/libzdoor/common/ztree.h @@ -0,0 +1,88 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2011 Joyent, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ZTREE_H +#define _ZTREE_H + +#include <zdoor.h> +#include <zone.h> +#include "zdoor-int.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct dtree_entry; + +typedef void (*ztree_door_visitor)(struct dtree_entry *entry); + +typedef struct ztree_entry { + char *zte_zonename; + zdoor_action_t zte_action; + int zte_num_doors; + void *zte_door_tree; + ztree_door_visitor zte_visitor; + struct zdoor_handle *zte_parent; +} ztree_entry_t; + +typedef struct dtree_entry { + char *dte_service; + int dte_door; + zdoor_callback dte_callback; + zdoor_cookie_t *dte_cookie; + ztree_entry_t *dte_parent; +} dtree_entry_t; + +#define ZTREE_SUCCESS 0 +#define ZTREE_ERROR -1 +#define ZTREE_ARGUMENT_ERROR -2 +#define ZTREE_ALREADY_EXISTS -3 +#define ZTREE_NOT_FOUND -4 + +extern int ztree_zone_add(struct zdoor_handle *handle, + const char *zonename, ztree_door_visitor visitor); + +extern ztree_entry_t *ztree_zone_find(struct zdoor_handle *handle, + const char *zonename); + +extern void ztree_zone_remove(struct zdoor_handle *handle, + ztree_entry_t *entry); + +extern int ztree_door_add(struct zdoor_handle *handle, const char *zonename, + const char *service, zdoor_callback callback, zdoor_cookie_t *cookie); + +extern dtree_entry_t *ztree_door_find(struct zdoor_handle *handle, + const char *zonename, const char *service); + +extern zdoor_cookie_t *ztree_door_remove(struct zdoor_handle *handle, + dtree_entry_t *entry); + +extern void ztree_walk_doors(struct zdoor_handle *handle, const char *zonename); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZTREE_H */ diff --git a/usr/src/lib/libzdoor/i386/Makefile b/usr/src/lib/libzdoor/i386/Makefile new file mode 100644 index 0000000000..2bfe8001d9 --- /dev/null +++ b/usr/src/lib/libzdoor/i386/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index 48692b1b1a..8d14d37866 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -65,6 +65,7 @@ typedef enum zfs_error { EZFS_PROPTYPE, /* property does not apply to dataset type */ EZFS_PROPNONINHERIT, /* property is not inheritable */ EZFS_PROPSPACE, /* bad quota or reservation */ + EZFS_PROPCACHED, /* prop unavail since cachedprops flag set */ EZFS_BADTYPE, /* dataset is not of appropriate type */ EZFS_BUSY, /* pool or dataset is busy */ EZFS_EXISTS, /* pool or dataset already exists */ @@ -214,6 +215,7 @@ extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *); extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *); extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t); +extern void libzfs_set_cachedprops(libzfs_handle_t *, boolean_t); extern void zfs_save_arguments(int argc, char **, char *, int); extern int zpool_log_history(libzfs_handle_t *, const char *); diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c index 4e5ef6f292..18f43453a2 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -320,6 +320,10 @@ get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc) { libzfs_handle_t *hdl = zhp->zfs_hdl; + if (hdl->libzfs_cachedprops && + libzfs_cmd_set_cachedprops(hdl, zc) != 0) + return (-1); + (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name)); while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) { @@ -2290,6 +2294,11 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src, case ZFS_PROP_NORMALIZE: case ZFS_PROP_UTF8ONLY: case ZFS_PROP_CASE: + if (zhp->zfs_hdl->libzfs_cachedprops) { + return (zfs_error(zhp->zfs_hdl, EZFS_PROPCACHED, + "property unavailable since not cached")); + } + if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) || zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) return (-1); @@ -2601,6 +2610,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, const char *str; const char *strval; boolean_t received = zfs_is_recvd_props_mode(zhp); + boolean_t printerr; /* * Check to see if this property applies to our object @@ -2611,6 +2621,16 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, if (received && zfs_prop_readonly(prop)) return (-1); + if (zhp->zfs_hdl->libzfs_cachedprops && + zfs_prop_cacheable(prop)) { + printerr = zhp->zfs_hdl->libzfs_printerr; + libzfs_print_on_error(zhp->zfs_hdl, B_FALSE); + (void) zfs_error(zhp->zfs_hdl, EZFS_PROPCACHED, + "property unavailable since not cached"); + libzfs_print_on_error(zhp->zfs_hdl, printerr); + return (-1); + } + if (src) *src = ZPROP_SRC_NONE; diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h index ed5d491b0a..aa420a41a2 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -22,6 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2011, 2017 by Delphix. All rights reserved. * Copyright 2019 Joyent, Inc. */ @@ -89,6 +90,7 @@ struct libzfs_handle { libzfs_fru_t **libzfs_fru_hash; libzfs_fru_t *libzfs_fru_list; char libzfs_chassis_id[256]; + boolean_t libzfs_cachedprops; boolean_t libzfs_prop_debug; di_devlink_handle_t libzfs_devlink; }; @@ -162,6 +164,7 @@ int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***, size_t *); zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *); zfs_handle_t *make_dataset_simple_handle_zc(zfs_handle_t *, zfs_cmd_t *); +int libzfs_cmd_set_cachedprops(libzfs_handle_t *, zfs_cmd_t *); int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t, nvlist_t *, char **, uint64_t *, const char *); diff --git a/usr/src/lib/libzfs/common/libzfs_iter.c b/usr/src/lib/libzfs/common/libzfs_iter.c index b90293fedd..9e160087b3 100644 --- a/usr/src/lib/libzfs/common/libzfs_iter.c +++ b/usr/src/lib/libzfs/common/libzfs_iter.c @@ -24,6 +24,7 @@ * Copyright (c) 2013, 2015 by Delphix. All rights reserved. * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -112,6 +113,10 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM) return (0); + if (zhp->zfs_hdl->libzfs_cachedprops && + libzfs_cmd_set_cachedprops(zhp->zfs_hdl, &zc) != 0) + return (-1); + if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) return (-1); @@ -122,9 +127,8 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data) * that the pool has since been removed. */ if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, - &zc)) == NULL) { + &zc)) == NULL) continue; - } if ((ret = func(nzhp, data)) != 0) { zcmd_free_nvlists(&zc); @@ -150,10 +154,17 @@ zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func, zhp->zfs_type == ZFS_TYPE_BOOKMARK) return (0); + + if (zhp->zfs_hdl->libzfs_cachedprops && + libzfs_cmd_set_cachedprops(zhp->zfs_hdl, &zc) != 0) + return (-1); + zc.zc_simple = simple; + if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0) return (-1); + while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc)) == 0) { diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index 60c83be1a9..4ec383d318 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -371,6 +371,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, default: (void) snprintf(buf, len, "%llu", intval); } + break; case PROP_TYPE_INDEX: @@ -1985,6 +1986,8 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, "corrupted, use '-m' to import the pool " "anyway:\n")); print_vdev_tree(hdl, NULL, missing, 2); + zpool_rewind_exclaim(hdl, newname ? origname : + thename, B_TRUE, nv); (void) printf("\n"); } (void) zpool_standard_error(hdl, error, desc); @@ -2018,8 +2021,9 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, ret = -1; else if (zhp != NULL) zpool_close(zhp); - if (policy.zlp_rewind & - (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) { + if ((policy.zlp_rewind & + (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) || + (flags & ZFS_IMPORT_MISSING_LOG)) { zpool_rewind_exclaim(hdl, newname ? origname : thename, ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0), nv); } diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index 3285cf7ce0..31d66e9469 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -87,6 +87,9 @@ libzfs_error_description(libzfs_handle_t *hdl) return (dgettext(TEXT_DOMAIN, "property cannot be inherited")); case EZFS_PROPSPACE: return (dgettext(TEXT_DOMAIN, "invalid quota or reservation")); + case EZFS_PROPCACHED: + return (dgettext(TEXT_DOMAIN, "property unavailable since " + "cachedprops flag set")); case EZFS_BADTYPE: return (dgettext(TEXT_DOMAIN, "operation not applicable to " "datasets of this type")); @@ -636,6 +639,42 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr) hdl->libzfs_printerr = printerr; } +/* + * Set the value of the cachedprops flag. If the cachedprops flag is set, + * operations which get ZFS properties will only retrieve a property if the + * property is cached somewhere in memory. + * + * Consumers of libzfs should take care when setting this flag, as they will + * prevent themselves from listing the full set of ZFS properties. + * + * ZFS properties which always require disk I/O are ZPL properties (utf8only, + * normalization, etc.) and the volsize and volblocksize properties for volumes. + */ +void +libzfs_set_cachedprops(libzfs_handle_t *hdl, boolean_t cachedprops) +{ + hdl->libzfs_cachedprops = cachedprops; +} + +/* + * Adds a src nvlist to a zfs_cmd_t which specifies that only cached (i.e., will + * not require a disk access) properties should be retrieved. + */ +int +libzfs_cmd_set_cachedprops(libzfs_handle_t *hdl, zfs_cmd_t *zc) +{ + nvlist_t *nvl; + int ret; + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 || + nvlist_add_boolean_value(nvl, "cachedpropsonly", B_TRUE) != 0) + return (no_memory(hdl)); + + ret = zcmd_write_src_nvlist(hdl, zc, nvl); + nvlist_free(nvl); + return (ret); +} + libzfs_handle_t * libzfs_init(void) { @@ -671,6 +710,7 @@ libzfs_init(void) zpool_feature_init(); libzfs_mnttab_init(hdl); + hdl->libzfs_cachedprops = B_FALSE; if (getenv("ZFS_PROP_DEBUG") != NULL) { hdl->libzfs_prop_debug = B_TRUE; } diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 49fc94a536..5ad0f9d4eb 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -21,6 +21,7 @@ # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # Copyright (c) 2011, 2017 by Delphix. All rights reserved. # Copyright 2016 Nexenta Systems, Inc. # Copyright 2020 Joyent, Inc. @@ -64,6 +65,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { libzfs_init; libzfs_mnttab_cache; libzfs_print_on_error; + libzfs_set_cachedprops; spa_feature_table; zfs_allocatable_devs; zfs_asprintf; @@ -116,6 +118,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zfs_path_to_zhandle; zfs_promote; zfs_prop_align_right; + zfs_prop_cacheable; zfs_prop_column_name; zfs_prop_default_numeric; zfs_prop_default_string; diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.c b/usr/src/lib/libzfs_core/common/libzfs_core.c index aad4104fa5..628b1cc8ff 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.c +++ b/usr/src/lib/libzfs_core/common/libzfs_core.c @@ -25,6 +25,7 @@ * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. * Copyright (c) 2017 Datto Inc. + * Copyright 2020 Joyent, Inc. */ /* @@ -1021,6 +1022,7 @@ lzc_channel_program_impl(const char *pool, const char *program, boolean_t sync, { int error; nvlist_t *args; + nvlist_t *hidden_args = NULL; args = fnvlist_alloc(); fnvlist_add_string(args, ZCP_ARG_PROGRAM, program); @@ -1028,6 +1030,22 @@ lzc_channel_program_impl(const char *pool, const char *program, boolean_t sync, fnvlist_add_boolean_value(args, ZCP_ARG_SYNC, sync); fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit); fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit); + + /* + * If any hidden arguments are passed, we pull them out of 'args' + * and into a separate nvlist so spa_history_nvl() doesn't log + * their values. + */ + if (nvlist_lookup_nvlist(argnvl, ZPOOL_HIDDEN_ARGS, + &hidden_args) == 0) { + nvlist_t *argcopy = fnvlist_dup(argnvl); + + fnvlist_add_nvlist(args, ZPOOL_HIDDEN_ARGS, hidden_args); + fnvlist_remove(argcopy, ZPOOL_HIDDEN_ARGS); + fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argcopy); + nvlist_free(argcopy); + } + error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl); fnvlist_free(args); diff --git a/usr/src/lib/libzonecfg/Makefile.com b/usr/src/lib/libzonecfg/Makefile.com index aa7dea9cc9..55b33a3e77 100644 --- a/usr/src/lib/libzonecfg/Makefile.com +++ b/usr/src/lib/libzonecfg/Makefile.com @@ -20,11 +20,14 @@ # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015 Joyent, Inc. # LIBRARY= libzonecfg.a VERS= .1 -OBJECTS= libzonecfg.o getzoneent.o scratchops.o +LIB_OBJS= libzonecfg.o getzoneent.o scratchops.o +XML_OBJS= os_dtd.o +OBJECTS= $(LIB_OBJS) $(XML_OBJS) include ../../Makefile.lib @@ -36,12 +39,24 @@ $(DYNLIB) := LDLIBS += -lxml2 NATIVE_LIBS += libxml2.so SRCDIR = ../common + +XMLDIR = $(SRC)/lib/xml +SRCS = \ + $(LIB_OBJS:%.o=$(SRCDIR)/%.c) \ + $(XML_OBJS:%.o=$(XMLDIR)/%.c) \ + CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include/libxml2 -I$(SRCDIR) -D_REENTRANT CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -_gcc=-Wno-parentheses +CPPFLAGS += -I$(XMLDIR) + .KEEP_STATE: all: $(LIBS) +pics/%.o: $(XMLDIR)/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + include ../../Makefile.targ diff --git a/usr/src/lib/libzonecfg/common/getzoneent.c b/usr/src/lib/libzonecfg/common/getzoneent.c index 8155f7272a..c9f1c12bcf 100644 --- a/usr/src/lib/libzonecfg/common/getzoneent.c +++ b/usr/src/lib/libzonecfg/common/getzoneent.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Joyent, Inc. */ @@ -127,6 +128,8 @@ getzoneent_private(FILE *cookie) /* skip comment lines */ continue; } + + /* zonename */ p = gettok(&cp); if (*p == '\0' || strlen(p) >= ZONENAME_MAX) { /* @@ -136,6 +139,7 @@ getzoneent_private(FILE *cookie) } (void) strlcpy(ze->zone_name, p, ZONENAME_MAX); + /* state */ p = gettok(&cp); if (*p == '\0') { /* state field should not be empty */ @@ -152,6 +156,7 @@ getzoneent_private(FILE *cookie) continue; } + /* zonepath */ p = gettok(&cp); if (strlen(p) >= MAXPATHLEN) { /* very long paths are not allowed */ @@ -159,10 +164,35 @@ getzoneent_private(FILE *cookie) } (void) strlcpy(ze->zone_path, p, MAXPATHLEN); + /* uuid */ p = gettok(&cp); if (uuid_parse(p, ze->zone_uuid) == -1) uuid_clear(ze->zone_uuid); + /* brand [optional] */ + p = gettok(&cp); + if (strlen(p) >= MAXNAMELEN) { + /* very long names are not allowed */ + continue; + } + (void) strlcpy(ze->zone_brand, p, MAXNAMELEN); + + /* IP type [optional] */ + p = gettok(&cp); + if (strlen(p) >= MAXNAMELEN) { + /* very long names are not allowed */ + continue; + } + ze->zone_iptype = ZS_SHARED; + if (*p == 'e') { + ze->zone_iptype = ZS_EXCLUSIVE; + } + + /* debug ID [optional] */ + p = gettok(&cp); + if (*p != '\0') + ze->zone_did = atoi(p); + break; } @@ -170,10 +200,32 @@ getzoneent_private(FILE *cookie) } static boolean_t -get_index_path(char *path) +path_common(char *path, size_t path_size, const char *stem) { - return (snprintf(path, MAXPATHLEN, "%s%s", zonecfg_root, - ZONE_INDEX_FILE) < MAXPATHLEN); + const char *native_root = zone_get_nroot(); + + if (native_root == NULL || zonecfg_in_alt_root()) { + /* + * Do not prepend the native system root (e.g. "/native") if an + * alternative configuration root has been selected. + */ + native_root = ""; + } + + return (snprintf(path, path_size, "%s%s%s", native_root, zonecfg_root, + stem) < path_size); +} + +static boolean_t +get_index_path(char *path, size_t path_size) +{ + return (path_common(path, path_size, ZONE_INDEX_FILE)); +} + +static boolean_t +get_temp_path(char *path, size_t path_size) +{ + return (path_common(path, path_size, _PATH_TMPFILE)); } FILE * @@ -181,7 +233,7 @@ setzoneent(void) { char path[MAXPATHLEN]; - if (!get_index_path(path)) { + if (!get_index_path(path, sizeof (path))) { errno = EINVAL; return (NULL); } @@ -202,8 +254,7 @@ lock_index_file(void) struct flock lock; char path[MAXPATHLEN]; - if (snprintf(path, sizeof (path), "%s%s", zonecfg_root, - ZONE_INDEX_LOCK_DIR) >= sizeof (path)) + if (!path_common(path, sizeof (path), ZONE_INDEX_LOCK_DIR)) return (-1); if ((mkdir(path, S_IRWXU) == -1) && errno != EEXIST) return (-1); @@ -269,14 +320,17 @@ int putzoneent(struct zoneent *ze, zoneent_op_t operation) { FILE *index_file, *tmp_file; - char *tmp_file_name, buf[MAX_INDEX_LEN]; + char buf[MAX_INDEX_LEN]; int tmp_file_desc, lock_fd, err; boolean_t exist, need_quotes; - char *cp; + char *cp, *tmpp; + char tmp_path[MAXPATHLEN]; char path[MAXPATHLEN]; char uuidstr[UUID_PRINTABLE_STRING_LENGTH]; - size_t tlen, namelen; - const char *zone_name, *zone_state, *zone_path, *zone_uuid; + size_t namelen; + const char *zone_name, *zone_state, *zone_path, *zone_uuid, + *zone_brand = "", *zone_iptype; + zoneid_t zone_did; assert(ze != NULL); @@ -299,20 +353,14 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) if ((lock_fd = lock_index_file()) == -1) return (Z_LOCKING_FILE); - /* using sizeof gives us room for the terminating NUL byte as well */ - tlen = sizeof (_PATH_TMPFILE) + strlen(zonecfg_root); - tmp_file_name = malloc(tlen); - if (tmp_file_name == NULL) { + if (!get_temp_path(tmp_path, sizeof (tmp_path))) { (void) unlock_index_file(lock_fd); return (Z_NOMEM); } - (void) snprintf(tmp_file_name, tlen, "%s%s", zonecfg_root, - _PATH_TMPFILE); - tmp_file_desc = mkstemp(tmp_file_name); + tmp_file_desc = mkstemp(tmp_path); if (tmp_file_desc == -1) { - (void) unlink(tmp_file_name); - free(tmp_file_name); + (void) unlink(tmp_path); (void) unlock_index_file(lock_fd); return (Z_TEMP_FILE); } @@ -323,7 +371,7 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) err = Z_MISC_FS; goto error; } - if (!get_index_path(path)) { + if (!get_index_path(path, sizeof (path))) { err = Z_MISC_FS; goto error; } @@ -335,6 +383,9 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) exist = B_FALSE; zone_name = ze->zone_name; namelen = strlen(zone_name); + zone_brand = ze->zone_brand; + zone_iptype = (ze->zone_iptype == ZS_SHARED ? "sh" : "ex"); + zone_did = ze->zone_did; for (;;) { if (fgets(buf, sizeof (buf), index_file) == NULL) { if (operation == PZE_ADD && !exist) { @@ -389,6 +440,11 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) } zone_path = gettok(&cp); zone_uuid = gettok(&cp); + zone_brand = gettok(&cp); + zone_iptype = gettok(&cp); + tmpp = gettok(&cp); + if (*tmpp != '\0') + zone_did = atoi(tmpp); switch (operation) { case PZE_ADD: @@ -403,14 +459,6 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) */ if (ze->zone_state >= 0) { zone_state = zone_state_str(ze->zone_state); - - /* - * If the caller is uninstalling this zone, - * then wipe out the uuid. The zone's contents - * are no longer known. - */ - if (ze->zone_state < ZONE_STATE_INSTALLED) - zone_uuid = ""; } /* If a new name is supplied, use it. */ @@ -419,6 +467,27 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) if (ze->zone_path[0] != '\0') zone_path = ze->zone_path; + + /* If new UUID provided, replace it */ + if (!uuid_is_null(ze->zone_uuid)) { + uuid_unparse(ze->zone_uuid, uuidstr); + zone_uuid = uuidstr; + } + + /* If a brand is supplied, use it. */ + if (ze->zone_brand[0] != '\0') { + zone_brand = ze->zone_brand; + + /* + * Since the brand, iptype and did are optional, + * we we only reset the iptype and did if the + * brand is provided. + */ + zone_iptype = (ze->zone_iptype == ZS_SHARED ? + "sh" : "ex"); + zone_did = ze->zone_did; + } + break; case PZE_REMOVE: @@ -450,9 +519,17 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) * method for escaping them. */ need_quotes = (strchr(zone_path, ':') != NULL); - (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name, - zone_state, need_quotes ? "\"" : "", zone_path, - need_quotes ? "\"" : "", zone_uuid); + + if (*zone_brand != '\0') { + (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s:%s:%s:%d\n", + zone_name, zone_state, need_quotes ? "\"" : "", + zone_path, need_quotes ? "\"" : "", zone_uuid, + zone_brand, zone_iptype, zone_did); + } else { + (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name, + zone_state, need_quotes ? "\"" : "", zone_path, + need_quotes ? "\"" : "", zone_uuid); + } exist = B_TRUE; } @@ -464,11 +541,10 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) goto error; } tmp_file = NULL; - if (rename(tmp_file_name, path) == -1) { + if (rename(tmp_path, path) == -1) { err = errno == EACCES ? Z_ACCES : Z_MISC_FS; goto error; } - free(tmp_file_name); if (unlock_index_file(lock_fd) != Z_OK) return (Z_UNLOCKING_FILE); return (Z_OK); @@ -478,8 +554,7 @@ error: (void) fclose(index_file); if (tmp_file != NULL) (void) fclose(tmp_file); - (void) unlink(tmp_file_name); - free(tmp_file_name); + (void) unlink(tmp_path); (void) unlock_index_file(lock_fd); return (err); } diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index b4eade5826..7dc3f9ccb8 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.c @@ -22,7 +22,7 @@ /* * Copyright 2014 Gary Mills * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, Joyent, Inc. + * Copyright 2019 Joyent, Inc. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ @@ -60,6 +60,8 @@ #include <secdb.h> #include <user_attr.h> #include <prof_attr.h> +#include <sys/debug.h> +#include <os_dtd.h> #include <arpa/inet.h> #include <netdb.h> @@ -80,6 +82,8 @@ #define ZONE_EVENT_PING_SUBCLASS "ping" #define ZONE_EVENT_PING_PUBLISHER "solaris" +#define DEBUGID_FILE "/etc/zones/did.txt" + /* Hard-code the DTD element/attribute/entity names just once, here. */ #define DTD_ELEM_ATTR (const xmlChar *) "attr" #define DTD_ELEM_COMMENT (const xmlChar *) "comment" @@ -87,6 +91,7 @@ #define DTD_ELEM_FS (const xmlChar *) "filesystem" #define DTD_ELEM_FSOPTION (const xmlChar *) "fsoption" #define DTD_ELEM_NET (const xmlChar *) "network" +#define DTD_ELEM_NETATTR (const xmlChar *) "net-attr" #define DTD_ELEM_RCTL (const xmlChar *) "rctl" #define DTD_ELEM_RCTLVALUE (const xmlChar *) "rctl-value" #define DTD_ELEM_ZONE (const xmlChar *) "zone" @@ -107,10 +112,12 @@ #define DTD_ATTR_IPTYPE (const xmlChar *) "ip-type" #define DTD_ATTR_DEFROUTER (const xmlChar *) "defrouter" #define DTD_ATTR_DIR (const xmlChar *) "directory" +#define DTD_ATTR_GNIC (const xmlChar *) "global-nic" #define DTD_ATTR_LIMIT (const xmlChar *) "limit" #define DTD_ATTR_LIMITPRIV (const xmlChar *) "limitpriv" #define DTD_ATTR_BOOTARGS (const xmlChar *) "bootargs" #define DTD_ATTR_SCHED (const xmlChar *) "scheduling-class" +#define DTD_ATTR_MAC (const xmlChar *) "mac-addr" #define DTD_ATTR_MATCH (const xmlChar *) "match" #define DTD_ATTR_NAME (const xmlChar *) "name" #define DTD_ATTR_PHYSICAL (const xmlChar *) "physical" @@ -120,6 +127,7 @@ #define DTD_ATTR_SPECIAL (const xmlChar *) "special" #define DTD_ATTR_TYPE (const xmlChar *) "type" #define DTD_ATTR_VALUE (const xmlChar *) "value" +#define DTD_ATTR_VLANID (const xmlChar *) "vlan-id" #define DTD_ATTR_ZONEPATH (const xmlChar *) "zonepath" #define DTD_ATTR_NCPU_MIN (const xmlChar *) "ncpu_min" #define DTD_ATTR_NCPU_MAX (const xmlChar *) "ncpu_max" @@ -132,6 +140,7 @@ #define DTD_ATTR_MODE (const xmlChar *) "mode" #define DTD_ATTR_ACL (const xmlChar *) "acl" #define DTD_ATTR_BRAND (const xmlChar *) "brand" +#define DTD_ATTR_DID (const xmlChar *) "debugid" #define DTD_ATTR_HOSTID (const xmlChar *) "hostid" #define DTD_ATTR_USER (const xmlChar *) "user" #define DTD_ATTR_AUTHS (const xmlChar *) "auths" @@ -182,9 +191,12 @@ static struct alias { {ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0}, {ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0}, {ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0}, + {ALIAS_MAXPHYSMEM, "zone.max-physical-memory", "privileged", "deny", + 1048576}, {ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0}, {ALIAS_CPUCAP, "zone.cpu-cap", "privileged", "deny", 0}, {ALIAS_MAXPROCS, "zone.max-processes", "privileged", "deny", 100}, + {ALIAS_ZFSPRI, "zone.zfs-io-priority", "privileged", "none", 0}, {NULL, NULL, NULL, NULL, 0} }; @@ -232,6 +244,8 @@ static int zone_lock_cnt = 0; static char zoneadm_lock_held[] = LOCK_ENV_VAR"=1"; static char zoneadm_lock_not_held[] = LOCK_ENV_VAR"=0"; +static void zonecfg_notify_delete(const char *); + char *zonecfg_root = ""; /* @@ -275,17 +289,35 @@ zonecfg_in_alt_root(void) */ static boolean_t -config_file_path(const char *zonename, char *answer) +file_path_common(const char *zonename, const char *subdir, const char *stem, + char *answer, size_t answer_size) { - return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root, - ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN); + const char *native_root = zone_get_nroot(); + + if (native_root == NULL || zonecfg_in_alt_root()) { + /* + * Do not prepend the native system root (e.g. "/native") if an + * alternative configuration root has been selected. + */ + native_root = ""; + } + + return (snprintf(answer, answer_size, "%s%s%s/%s.%s", native_root, + zonecfg_root, subdir, zonename, stem) < answer_size); } static boolean_t -snap_file_path(const char *zonename, char *answer) +config_file_path(const char *zonename, char *answer, size_t answer_size) { - return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml", - zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN); + return (file_path_common(zonename, ZONE_CONFIG_ROOT, "xml", answer, + answer_size)); +} + +static boolean_t +snap_file_path(const char *zonename, char *answer, size_t answer_size) +{ + return (file_path_common(zonename, ZONE_SNAPSHOT_ROOT, "snapshot.xml", + answer, answer_size)); } /*ARGSUSED*/ @@ -356,7 +388,7 @@ zonecfg_destroy(const char *zonename, boolean_t force) int err, state_err; zone_state_t state; - if (!config_file_path(zonename, path)) + if (!config_file_path(zonename, path, sizeof (path))) return (Z_MISC_FS); state_err = zone_get_state((char *)zonename, &state); @@ -403,8 +435,10 @@ zonecfg_destroy(const char *zonename, boolean_t force) * Treat failure to find the XML file silently, since, well, it's * gone, and with the index file cleaned up, we're done. */ - if (err == Z_OK || err == Z_NO_ZONE) + if (err == Z_OK || err == Z_NO_ZONE) { + zonecfg_notify_delete(zonename); return (Z_OK); + } return (err); } @@ -413,7 +447,7 @@ zonecfg_destroy_snapshot(const char *zonename) { char path[MAXPATHLEN]; - if (!snap_file_path(zonename, path)) + if (!snap_file_path(zonename, path, sizeof (path))) return (Z_MISC_FS); return (zonecfg_destroy_impl(path)); } @@ -580,9 +614,8 @@ static int zonecfg_get_handle_impl(const char *zonename, const char *filename, zone_dochandle_t handle) { - xmlValidCtxtPtr cvp; struct stat statbuf; - int valid; + boolean_t valid; if (zonename == NULL) return (Z_NO_ZONE); @@ -593,14 +626,13 @@ zonecfg_get_handle_impl(const char *zonename, const char *filename, return (Z_INVALID_DOCUMENT); return (Z_NO_ZONE); } - if ((cvp = xmlNewValidCtxt()) == NULL) + + if (os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid) != 0) { return (Z_NOMEM); - cvp->error = zonecfg_error_func; - cvp->warning = zonecfg_error_func; - valid = xmlValidateDocument(cvp, handle->zone_dh_doc); - xmlFreeValidCtxt(cvp); - if (valid == 0) + } + if (!valid) { return (Z_INVALID_DOCUMENT); + } /* delete any comments such as inherited Sun copyright / ident str */ stripcomments(handle); @@ -612,7 +644,7 @@ zonecfg_get_handle(const char *zonename, zone_dochandle_t handle) { char path[MAXPATHLEN]; - if (!config_file_path(zonename, path)) + if (!config_file_path(zonename, path, sizeof (path))) return (Z_MISC_FS); handle->zone_dh_newzone = B_FALSE; @@ -656,7 +688,7 @@ zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle) { char path[MAXPATHLEN]; - if (!snap_file_path(zonename, path)) + if (!snap_file_path(zonename, path, sizeof (path))) return (Z_MISC_FS); handle->zone_dh_newzone = B_FALSE; return (zonecfg_get_handle_impl(zonename, path, handle)); @@ -669,7 +701,7 @@ zonecfg_get_template_handle(const char *template, const char *zonename, char path[MAXPATHLEN]; int err; - if (!config_file_path(template, path)) + if (!config_file_path(template, path, sizeof (path))) return (Z_MISC_FS); if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK) @@ -703,21 +735,19 @@ int zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle, zone_dochandle_t rem_handle) { - xmlValidCtxtPtr cvp; - int valid; + boolean_t valid; /* load the manifest into the handle for the remote system */ if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) { return (Z_INVALID_DOCUMENT); } - if ((cvp = xmlNewValidCtxt()) == NULL) + + if (os_dtd_validate(rem_handle->zone_dh_doc, B_FALSE, &valid) != 0) { return (Z_NOMEM); - cvp->error = zonecfg_error_func; - cvp->warning = zonecfg_error_func; - valid = xmlValidateDocument(cvp, rem_handle->zone_dh_doc); - xmlFreeValidCtxt(cvp); - if (valid == 0) + } + if (!valid) { return (Z_INVALID_DOCUMENT); + } /* delete any comments such as inherited Sun copyright / ident str */ stripcomments(rem_handle); @@ -738,14 +768,12 @@ zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle, * We need to re-run xmlValidateDocument on local_handle to properly * update the in-core representation of the configuration. */ - if ((cvp = xmlNewValidCtxt()) == NULL) + if (os_dtd_validate(local_handle->zone_dh_doc, B_FALSE, &valid) != 0) { return (Z_NOMEM); - cvp->error = zonecfg_error_func; - cvp->warning = zonecfg_error_func; - valid = xmlValidateDocument(cvp, local_handle->zone_dh_doc); - xmlFreeValidCtxt(cvp); - if (valid == 0) + } + if (!valid) { return (Z_INVALID_DOCUMENT); + } strip_sw_inv(local_handle); @@ -1107,7 +1135,7 @@ zonecfg_set_sched(zone_dochandle_t handle, char *sched) * In general, the operation of this function should succeed or fail as * a unit. */ -int +static int zonecfg_refresh_index_file(zone_dochandle_t handle) { char name[ZONENAME_MAX], zonepath[MAXPATHLEN]; @@ -1129,6 +1157,15 @@ zonecfg_refresh_index_file(zone_dochandle_t handle) (void) strlcpy(ze.zone_path, zonepath + strlen(zonecfg_root), sizeof (ze.zone_path)); + if ((err = zonecfg_get_brand(handle, ze.zone_brand, + sizeof (ze.zone_brand))) != 0) + return (err); + + if ((err = zonecfg_get_iptype(handle, &ze.zone_iptype)) != Z_OK) + return (err); + + ze.zone_did = zonecfg_get_did(handle); + if (is_renaming(handle)) { opcode = PZE_MODIFY; (void) strlcpy(ze.zone_name, handle->zone_dh_delete_name, @@ -1199,9 +1236,9 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename) { char tmpfile[MAXPATHLEN]; char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN]; - int tmpfd, err, valid; - xmlValidCtxt cvp = { NULL }; + int tmpfd, err; boolean_t backup; + boolean_t valid; (void) strlcpy(tmpfile, filename, sizeof (tmpfile)); (void) dirname(tmpfile); @@ -1214,16 +1251,13 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename) } (void) close(tmpfd); - cvp.error = zonecfg_error_func; - cvp.warning = zonecfg_error_func; - /* * We do a final validation of the document. Since the library has * malfunctioned if it fails to validate, we follow-up with an * assert() that the doc is valid. */ - valid = xmlValidateDocument(&cvp, handle->zone_dh_doc); - assert(valid != 0); + VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid)); + VERIFY(valid == B_TRUE); if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0) goto err; @@ -1278,7 +1312,6 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename) /* * Try to restore from our backup. */ - (void) unlink(filename); (void) rename(bakfile, filename); } else { /* @@ -1321,7 +1354,7 @@ zonecfg_save(zone_dochandle_t handle) if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK) return (err); - if (!config_file_path(zname, path)) + if (!config_file_path(zname, path, sizeof (path))) return (Z_MISC_FS); addcomment(handle, "\n DO NOT EDIT THIS " @@ -1342,8 +1375,10 @@ zonecfg_save(zone_dochandle_t handle) handle->zone_dh_newzone = B_FALSE; if (is_renaming(handle)) { - if (config_file_path(handle->zone_dh_delete_name, delpath)) + if (config_file_path(handle->zone_dh_delete_name, delpath, + sizeof (delpath))) { (void) unlink(delpath); + } handle->zone_dh_delete_name[0] = '\0'; } @@ -1353,23 +1388,18 @@ zonecfg_save(zone_dochandle_t handle) int zonecfg_verify_save(zone_dochandle_t handle, char *filename) { - int valid; - - xmlValidCtxt cvp = { NULL }; + boolean_t valid; if (zonecfg_check_handle(handle) != Z_OK) return (Z_BAD_HANDLE); - cvp.error = zonecfg_error_func; - cvp.warning = zonecfg_error_func; - /* * We do a final validation of the document. Since the library has * malfunctioned if it fails to validate, we follow-up with an * assert() that the doc is valid. */ - valid = xmlValidateDocument(&cvp, handle->zone_dh_doc); - assert(valid != 0); + VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid)); + VERIFY(valid == B_TRUE); if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0) return (Z_SAVING_FILE); @@ -1383,9 +1413,8 @@ zonecfg_detach_save(zone_dochandle_t handle, uint_t flags) char zname[ZONENAME_MAX]; char path[MAXPATHLEN]; char migpath[MAXPATHLEN]; - xmlValidCtxt cvp = { NULL }; int err = Z_SAVING_FILE; - int valid; + boolean_t valid; if (zonecfg_check_handle(handle) != Z_OK) return (Z_BAD_HANDLE); @@ -1412,16 +1441,13 @@ zonecfg_detach_save(zone_dochandle_t handle, uint_t flags) addcomment(handle, "\n DO NOT EDIT THIS FILE. " "Use zonecfg(1M) and zoneadm(1M) attach.\n"); - cvp.error = zonecfg_error_func; - cvp.warning = zonecfg_error_func; - /* * We do a final validation of the document. Since the library has * malfunctioned if it fails to validate, we follow-up with an * assert() that the doc is valid. */ - valid = xmlValidateDocument(&cvp, handle->zone_dh_doc); - assert(valid != 0); + VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid)); + VERIFY(valid == B_TRUE); if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0) return (Z_SAVING_FILE); @@ -1482,6 +1508,56 @@ zonecfg_rm_detached(zone_dochandle_t handle, boolean_t forced) } } +static void +zonecfg_notify_conf_change(const char *zname, char *os, char *ns) +{ + evchan_t *ze_chan; + struct timeval now; + uint64_t t; + nvlist_t *nvl = NULL; + + if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &ze_chan, 0) != 0) + return; + + /* Current time since Jan 1 1970 but consumers expect NS */ + (void) gettimeofday(&now, NULL); + t = (now.tv_sec * NANOSEC) + (now.tv_usec * 1000); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0 && + nvlist_add_string(nvl, ZONE_CB_NAME, zname) == 0 && + nvlist_add_string(nvl, ZONE_CB_NEWSTATE, ns) == 0 && + nvlist_add_string(nvl, ZONE_CB_OLDSTATE, os) == 0 && + nvlist_add_int32(nvl, ZONE_CB_ZONEID, -1) == 0 && + nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, t) == 0) { + (void) sysevent_evc_publish(ze_chan, ZONE_EVENT_STATUS_CLASS, + ZONE_EVENT_STATUS_SUBCLASS, "sun.com", "zonecfg", nvl, + EVCH_SLEEP); + } + + nvlist_free(nvl); + (void) sysevent_evc_unbind(ze_chan); +} + +void +zonecfg_notify_create(zone_dochandle_t handle) +{ + char zname[ZONENAME_MAX]; + + if (zonecfg_check_handle(handle) != Z_OK) + return; + + if (zonecfg_get_name(handle, zname, sizeof (zname)) != Z_OK) + return; + + zonecfg_notify_conf_change(zname, "", ZONE_STATE_STR_CONFIGURED); +} + +static void +zonecfg_notify_delete(const char *zname) +{ + zonecfg_notify_conf_change(zname, ZONE_STATE_STR_CONFIGURED, ""); +} + /* * Special case: if access(2) fails with ENOENT, then try again using * ZONE_CONFIG_ROOT instead of config_file_path(zonename). This is how we @@ -1494,7 +1570,7 @@ zonecfg_access(const char *zonename, int amode) { char path[MAXPATHLEN]; - if (!config_file_path(zonename, path)) + if (!config_file_path(zonename, path, sizeof (path))) return (Z_INVAL); if (access(path, amode) == 0) return (Z_OK); @@ -1559,7 +1635,7 @@ zonecfg_create_snapshot(const char *zonename) goto out; } - if (!snap_file_path(zonename, path)) { + if (!snap_file_path(zonename, path, sizeof (path))) { error = Z_MISC_FS; goto out; } @@ -2072,6 +2148,32 @@ zonecfg_ifname_exists(sa_family_t af, char *ifname) } /* + * Turn an addr that looks like f:2:0:44:5:6C into 0f:02:00:44:05:6c + * We're expecting a dst of at least MAXMACADDRLEN size here. + */ +static void +normalize_mac_addr(char *dst, const char *src, int len) +{ + char *p, *e, *sep = ""; + long n; + char buf[MAXMACADDRLEN], tmp[4]; + + *dst = '\0'; + (void) strlcpy(buf, src, sizeof (buf)); + p = strtok(buf, ":"); + while (p != NULL) { + n = strtol(p, &e, 16); + if (*e != '\0' || n > 0xff) + return; + (void) snprintf(tmp, sizeof (tmp), "%s%02x", sep, n); + (void) strlcat(dst, tmp, len); + + sep = ":"; + p = strtok(NULL, ":"); + } +} + +/* * Determines whether there is a net resource with the physical interface, IP * address, and default router specified by 'tabptr' in the zone configuration * to which 'handle' refers. 'tabptr' must have an interface, an address, a @@ -2090,13 +2192,18 @@ zonecfg_ifname_exists(sa_family_t af, char *ifname) int zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) { - xmlNodePtr cur; + xmlNodePtr cur, val; xmlNodePtr firstmatch; int err; char address[INET6_ADDRSTRLEN]; char physical[LIFNAMSIZ]; + char mac[MAXMACADDRLEN]; + char norm_mac[MAXMACADDRLEN]; + char gnic[LIFNAMSIZ]; size_t addrspec; /* nonzero if tabptr has IP addr */ size_t physspec; /* nonzero if tabptr has interface */ + size_t macspec; /* nonzero if tabptr has mac addr */ + size_t gnicspec; /* nonzero if tabptr has gnic */ size_t defrouterspec; /* nonzero if tabptr has def. router */ size_t allowed_addrspec; zone_iptype_t iptype; @@ -2108,17 +2215,20 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) * Determine the fields that will be searched. There must be at least * one. * - * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are + * zone_nwif_address, zone_nwif_physical, zone_nwif_defrouter, + * zone_nwif_mac, zone_nwif_vlan_id and zone_nwif_gnic are * arrays, so no NULL checks are necessary. */ addrspec = strlen(tabptr->zone_nwif_address); physspec = strlen(tabptr->zone_nwif_physical); + macspec = strlen(tabptr->zone_nwif_mac); + gnicspec = strlen(tabptr->zone_nwif_gnic); defrouterspec = strlen(tabptr->zone_nwif_defrouter); allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address); if (addrspec != 0 && allowed_addrspec != 0) return (Z_INVAL); /* can't specify both */ if (addrspec == 0 && physspec == 0 && defrouterspec == 0 && - allowed_addrspec == 0) + allowed_addrspec == 0 && macspec == 0 && gnicspec == 0) return (Z_INSUFFICIENT_SPEC); if ((err = operation_prep(handle)) != Z_OK) @@ -2145,6 +2255,19 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) physical, sizeof (physical)) != Z_OK || strcmp(tabptr->zone_nwif_physical, physical) != 0)) continue; + if (iptype == ZS_EXCLUSIVE && macspec != 0) { + if (fetchprop(cur, DTD_ATTR_MAC, mac, sizeof (mac)) != + Z_OK) + continue; + normalize_mac_addr(norm_mac, mac, sizeof (norm_mac)); + if (strcmp(tabptr->zone_nwif_mac, norm_mac) != 0) + continue; + } + if (iptype == ZS_EXCLUSIVE && gnicspec != 0 && + (fetchprop(cur, DTD_ATTR_GNIC, gnic, + sizeof (gnic)) != Z_OK || + strcmp(tabptr->zone_nwif_gnic, gnic) != 0)) + continue; if (iptype == ZS_SHARED && addrspec != 0 && (fetchprop(cur, DTD_ATTR_ADDRESS, address, sizeof (address)) != Z_OK || @@ -2187,6 +2310,21 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) return (err); if (iptype == ZS_EXCLUSIVE && + (err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac, + sizeof (tabptr->zone_nwif_mac))) != Z_OK) + return (err); + + if (iptype == ZS_EXCLUSIVE && + (err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id, + sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK) + return (err); + + if (iptype == ZS_EXCLUSIVE && + (err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic, + sizeof (tabptr->zone_nwif_gnic))) != Z_OK) + return (err); + + if (iptype == ZS_EXCLUSIVE && (err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS, tabptr->zone_nwif_allowed_address, sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK) @@ -2197,13 +2335,40 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) return (err); + tabptr->zone_nwif_attrp = NULL; + for (val = cur->xmlChildrenNode; val != NULL; val = val->next) { + struct zone_res_attrtab *valptr; + + valptr = (struct zone_res_attrtab *)malloc( + sizeof (struct zone_res_attrtab)); + if (valptr == NULL) + return (Z_NOMEM); + + valptr->zone_res_attr_name[0] = + valptr->zone_res_attr_value[0] = '\0'; + if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr) + != Z_OK) { + free(valptr); + break; + } + + if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name, + sizeof (valptr->zone_res_attr_name)) != Z_OK)) + break; + if ((fetchprop(val, DTD_ATTR_VALUE, + valptr->zone_res_attr_value, + sizeof (valptr->zone_res_attr_value)) != Z_OK)) + break; + } + return (Z_OK); } static int zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) { - xmlNodePtr newnode, cur = handle->zone_dh_cur; + xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode; + struct zone_res_attrtab *valptr; int err; newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL); @@ -2219,13 +2384,40 @@ zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) tabptr->zone_nwif_physical)) != Z_OK) return (err); /* - * Do not add this property when it is not set, for backwards - * compatibility and because it is optional. + * Do not add these properties when they are not set, for backwards + * compatibility and because they are optional. */ if ((strlen(tabptr->zone_nwif_defrouter) > 0) && ((err = newprop(newnode, DTD_ATTR_DEFROUTER, tabptr->zone_nwif_defrouter)) != Z_OK)) return (err); + if (strlen(tabptr->zone_nwif_mac) > 0 && + (err = newprop(newnode, DTD_ATTR_MAC, + tabptr->zone_nwif_mac)) != Z_OK) + return (err); + if (strlen(tabptr->zone_nwif_vlan_id) > 0 && + (err = newprop(newnode, DTD_ATTR_VLANID, + tabptr->zone_nwif_vlan_id)) != Z_OK) + return (err); + if (strlen(tabptr->zone_nwif_gnic) > 0 && + (err = newprop(newnode, DTD_ATTR_GNIC, + tabptr->zone_nwif_gnic)) != Z_OK) + return (err); + + for (valptr = tabptr->zone_nwif_attrp; valptr != NULL; + valptr = valptr->zone_res_attr_next) { + valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR, + NULL); + err = newprop(valnode, DTD_ATTR_NAME, + valptr->zone_res_attr_name); + if (err != Z_OK) + return (err); + err = newprop(valnode, DTD_ATTR_VALUE, + valptr->zone_res_attr_value); + if (err != Z_OK) + return (err); + } + return (Z_OK); } @@ -2250,7 +2442,8 @@ static int zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) { xmlNodePtr cur = handle->zone_dh_cur; - boolean_t addr_match, phys_match, allowed_addr_match; + boolean_t addr_match, phys_match, allowed_addr_match, mac_match, + gnic_match; for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { if (xmlStrcmp(cur->name, DTD_ELEM_NET)) @@ -2262,8 +2455,13 @@ zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) tabptr->zone_nwif_allowed_address); phys_match = match_prop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical); + mac_match = match_prop(cur, DTD_ATTR_MAC, + tabptr->zone_nwif_mac); + gnic_match = match_prop(cur, DTD_ATTR_GNIC, + tabptr->zone_nwif_gnic); - if (addr_match && allowed_addr_match && phys_match) { + if (((addr_match && allowed_addr_match) || mac_match || + gnic_match) && phys_match) { xmlUnlinkNode(cur); xmlFreeNode(cur); return (Z_OK); @@ -2312,6 +2510,58 @@ zonecfg_modify_nwif( return (Z_OK); } +void +zonecfg_free_res_attr_list(struct zone_res_attrtab *valtab) +{ + if (valtab == NULL) + return; + zonecfg_free_res_attr_list(valtab->zone_res_attr_next); + free(valtab); +} + +int +zonecfg_add_res_attr(struct zone_res_attrtab **headptr, + struct zone_res_attrtab *valtabptr) +{ + struct zone_res_attrtab *last, *old, *new; + + last = *headptr; + for (old = last; old != NULL; old = old->zone_res_attr_next) + last = old; /* walk to the end of the list */ + new = valtabptr; /* alloc'd by caller */ + new->zone_res_attr_next = NULL; + if (last == NULL) + *headptr = new; + else + last->zone_res_attr_next = new; + return (Z_OK); +} + +int +zonecfg_remove_res_attr(struct zone_res_attrtab **headptr, + struct zone_res_attrtab *valtabptr) +{ + struct zone_res_attrtab *last, *this, *next; + + last = *headptr; + for (this = last; this != NULL; this = this->zone_res_attr_next) { + if (strcmp(this->zone_res_attr_name, + valtabptr->zone_res_attr_name) == 0 && + strcmp(this->zone_res_attr_value, + valtabptr->zone_res_attr_value) == 0) { + next = this->zone_res_attr_next; + if (this == *headptr) + *headptr = next; + else + last->zone_res_attr_next = next; + free(this); + return (Z_OK); + } else + last = this; + } + return (Z_NO_PROPERTY_ID); +} + /* * Must be a comma-separated list of alpha-numeric file system names. */ @@ -2461,7 +2711,7 @@ zonecfg_set_hostid(zone_dochandle_t handle, const char *hostidp) int zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr) { - xmlNodePtr cur, firstmatch; + xmlNodePtr cur, val, firstmatch; int err; char match[MAXPATHLEN]; @@ -2506,13 +2756,40 @@ zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr) sizeof (tabptr->zone_dev_match))) != Z_OK) return (err); + tabptr->zone_dev_attrp = NULL; + for (val = cur->xmlChildrenNode; val != NULL; val = val->next) { + struct zone_res_attrtab *valptr; + + valptr = (struct zone_res_attrtab *)malloc( + sizeof (struct zone_res_attrtab)); + if (valptr == NULL) + return (Z_NOMEM); + + valptr->zone_res_attr_name[0] = + valptr->zone_res_attr_value[0] = '\0'; + if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr) + != Z_OK) { + free(valptr); + break; + } + + if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name, + sizeof (valptr->zone_res_attr_name)) != Z_OK)) + break; + if ((fetchprop(val, DTD_ATTR_VALUE, + valptr->zone_res_attr_value, + sizeof (valptr->zone_res_attr_value)) != Z_OK)) + break; + } + return (Z_OK); } static int zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr) { - xmlNodePtr newnode, cur = handle->zone_dh_cur; + xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode; + struct zone_res_attrtab *valptr; int err; newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL); @@ -2521,6 +2798,21 @@ zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr) tabptr->zone_dev_match)) != Z_OK) return (err); + for (valptr = tabptr->zone_dev_attrp; valptr != NULL; + valptr = valptr->zone_res_attr_next) { + valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR, + NULL); + err = newprop(valnode, DTD_ATTR_NAME, + valptr->zone_res_attr_name); + if (err != Z_OK) + return (err); + err = newprop(valnode, DTD_ATTR_VALUE, + valptr->zone_res_attr_value); + if (err != Z_OK) + return (err); + } + + return (Z_OK); } @@ -4735,7 +5027,7 @@ get_pool_sched_class(char *poolname, char *class, int clsize) pool_conf_t *poolconf; pool_t *pool; pool_elem_t *pe; - pool_value_t *pv = pool_value_alloc(); + pool_value_t *pv; const char *sched_str; if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) @@ -4756,15 +5048,23 @@ get_pool_sched_class(char *poolname, char *class, int clsize) return (Z_NO_POOL); } + if ((pv = pool_value_alloc()) == NULL) { + (void) pool_conf_close(poolconf); + pool_conf_free(poolconf); + return (Z_NO_POOL); + } + pe = pool_to_elem(poolconf, pool); if (pool_get_property(poolconf, pe, "pool.scheduler", pv) != POC_STRING) { (void) pool_conf_close(poolconf); + pool_value_free(pv); pool_conf_free(poolconf); return (Z_NO_ENTRY); } (void) pool_value_get_string(pv, &sched_str); (void) pool_conf_close(poolconf); + pool_value_free(pv); pool_conf_free(poolconf); if (strlcpy(class, sched_str, clsize) >= clsize) return (Z_TOO_BIG); @@ -4873,7 +5173,8 @@ zonecfg_setnwifent(zone_dochandle_t handle) int zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr) { - xmlNodePtr cur; + xmlNodePtr cur, val; + struct zone_res_attrtab *valptr; int err; if (handle == NULL) @@ -4909,6 +5210,24 @@ zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr) return (err); } + if ((err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac, + sizeof (tabptr->zone_nwif_mac))) != Z_OK) { + handle->zone_dh_cur = handle->zone_dh_top; + return (err); + } + + if ((err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id, + sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK) { + handle->zone_dh_cur = handle->zone_dh_top; + return (err); + } + + if ((err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic, + sizeof (tabptr->zone_nwif_gnic))) != Z_OK) { + handle->zone_dh_cur = handle->zone_dh_top; + return (err); + } + if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER, tabptr->zone_nwif_defrouter, sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) { @@ -4916,6 +5235,29 @@ zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr) return (err); } + tabptr->zone_nwif_attrp = NULL; + for (val = cur->xmlChildrenNode; val != NULL; val = val->next) { + valptr = (struct zone_res_attrtab *)malloc( + sizeof (struct zone_res_attrtab)); + if (valptr == NULL) + return (Z_NOMEM); + + valptr->zone_res_attr_name[0] = + valptr->zone_res_attr_value[0] = '\0'; + if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr) + != Z_OK) { + free(valptr); + break; + } + + if (fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name, + sizeof (valptr->zone_res_attr_name)) != Z_OK) + break; + if (fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value, + sizeof (valptr->zone_res_attr_value)) != Z_OK) + break; + } + handle->zone_dh_cur = cur->next; return (Z_OK); } @@ -4935,7 +5277,7 @@ zonecfg_setdevent(zone_dochandle_t handle) int zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr) { - xmlNodePtr cur; + xmlNodePtr cur, val; int err; if (handle == NULL) @@ -4958,6 +5300,31 @@ zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr) return (err); } + tabptr->zone_dev_attrp = NULL; + for (val = cur->xmlChildrenNode; val != NULL; val = val->next) { + struct zone_res_attrtab *valptr; + + valptr = (struct zone_res_attrtab *)malloc( + sizeof (struct zone_res_attrtab)); + if (valptr == NULL) + return (Z_NOMEM); + + valptr->zone_res_attr_name[0] = + valptr->zone_res_attr_value[0] = '\0'; + if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr) + != Z_OK) { + free(valptr); + break; + } + + if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name, + sizeof (valptr->zone_res_attr_name)) != Z_OK)) + break; + if ((fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value, + sizeof (valptr->zone_res_attr_value)) != Z_OK)) + break; + } + handle->zone_dh_cur = cur->next; return (Z_OK); } @@ -5686,6 +6053,171 @@ zone_get_brand(char *zone_name, char *brandname, size_t rp_sz) } /* + * Atomically get a new zone_did value. The currently allocated value + * is stored in /etc/zones/did.txt. Lock the file, read the current value, + * increment, save the new value and unlock the file. Return the new value + * or -1 if there was an error. The ID namespace is large enough that we + * don't worry about recycling an ID when a zone is deleted. + */ +static zoneid_t +new_zone_did() +{ + int fd; + int len; + int val; + struct flock lck; + char pathbuf[PATH_MAX]; + char buf[80]; + + if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(), + DEBUGID_FILE) >= sizeof (pathbuf)) { + printf(gettext("alternate root path is too long")); + return (-1); + } + + if ((fd = open(pathbuf, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + perror("new_zone_did open failed"); + return (-1); + } + + /* Initialize the lock. */ + lck.l_whence = SEEK_SET; + lck.l_start = 0; + lck.l_len = 0; + + /* Wait until we acquire an exclusive lock on the file. */ + lck.l_type = F_WRLCK; + if (fcntl(fd, F_SETLKW, &lck) == -1) { + perror("new_zone_did lock failed"); + (void) close(fd); + return (-1); + } + + /* Get currently allocated value */ + len = read(fd, buf, sizeof (buf)); + if (len == -1) { + perror("new_zone_did read failed"); + val = -1; + } else { + if (lseek(fd, 0L, SEEK_SET) == -1) { + perror("new_zone_did seek failed"); + val = -1; + } else { + if (len == 0) { + /* Just created the file, initialize at 1 */ + val = 1; + } else { + val = atoi(buf); + val++; + } + + (void) snprintf(buf, sizeof (buf), "%d\n", val); + len = strlen(buf); + + /* Save newly allocated value */ + if (write(fd, buf, len) == -1) { + perror("new_zone_did write failed"); + val = -1; + } + } + } + + /* Release the file lock. */ + lck.l_type = F_UNLCK; + if (fcntl(fd, F_SETLK, &lck) == -1) { + perror("new_zone_did unlock failed"); + val = -1; + } + + if (close(fd) != 0) + perror("new_zone_did close failed"); + + return (val); +} + +/* + * Called by zoneadmd to get the zone's debug ID. + * If the zone doesn't already have an ID, a new one is generated and + * persistently saved onto the zone. Normally either zoneadm or zonecfg + * will assign a new ID for the zone, so zoneadmd should never have to + * generate one, but we also handle that here just to be paranoid. + */ +zoneid_t +zone_get_did(char *zone_name) +{ + int res; + zoneid_t new_did; + zone_dochandle_t handle; + char did_str[80]; + + if ((handle = zonecfg_init_handle()) == NULL) + return (getpid()); + + if (zonecfg_get_handle((char *)zone_name, handle) != Z_OK) + return (getpid()); + + res = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str)); + + /* If the zone already has an assigned debug ID, return it. */ + if (res == Z_OK && did_str[0] != '\0') { + zonecfg_fini_handle(handle); + return (atoi(did_str)); + } + + /* + * The zone doesn't have an assigned debug ID yet, generate one and + * save it as part of the zone definition. + */ + if ((new_did = new_zone_did()) == -1) { + /* + * We should really never hit this block of code. + * Generating a new ID failed for some reason. Use the current + * pid as a temporary ID so that the zone can continue to boot + * but we don't persistently save this temporary ID on the zone. + */ + zonecfg_fini_handle(handle); + return (getpid()); + } + + /* Now persistently save this new ID onto the zone. */ + (void) snprintf(did_str, sizeof (did_str), "%d", new_did); + (void) setrootattr(handle, DTD_ATTR_DID, did_str); + (void) zonecfg_save(handle); + + zonecfg_fini_handle(handle); + return (new_did); +} + +zoneid_t +zonecfg_get_did(zone_dochandle_t handle) +{ + char did_str[80]; + int err; + zoneid_t did; + + err = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str)); + if (err == Z_OK && did_str[0] != '\0') + did = atoi(did_str); + else + did = -1; + + return (did); +} + +void +zonecfg_set_did(zone_dochandle_t handle) +{ + zoneid_t new_did; + char did_str[80]; + + if ((new_did = new_zone_did()) == -1) + return; + (void) snprintf(did_str, sizeof (did_str), "%d", new_did); + (void) setrootattr(handle, DTD_ATTR_DID, did_str); +} + +/* * Return the appropriate root for the active /dev. * For normal zone, the path is $ZONEPATH/root; * for scratch zone, the dev path is $ZONEPATH/lu. @@ -5809,16 +6341,27 @@ int zone_set_state(char *zone, zone_state_t state) { struct zoneent ze; + int res; + zone_state_t oldst = (zone_state_t)-1; if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED && state != ZONE_STATE_INCOMPLETE) return (Z_INVAL); + (void) zone_get_state(zone, &oldst); + bzero(&ze, sizeof (ze)); (void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name)); ze.zone_state = state; (void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path)); - return (putzoneent(&ze, PZE_MODIFY)); + res = putzoneent(&ze, PZE_MODIFY); + + if (res == Z_OK) { + zonecfg_notify_conf_change(zone, zone_state_str(oldst), + zone_state_str(state)); + } + + return (res); } /* @@ -5968,6 +6511,30 @@ zonecfg_get_uuid(const char *zonename, uuid_t uuid) } /* + * Changes a zone's UUID to the given value. Returns an error if the UUID is + * malformed or if the zone cannot be located. + */ +int +zonecfg_set_uuid(const char *zonename, const char *zonepath, + const char *uuid) +{ + int err; + struct zoneent ze; + + bzero(&ze, sizeof (ze)); + ze.zone_state = -1; /* Preserve existing state in index */ + (void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name)); + (void) strlcpy(ze.zone_path, zonepath, sizeof (ze.zone_path)); + if (uuid_parse((char *)uuid, ze.zone_uuid) == -1) + return (Z_INVALID_PROPERTY); + + if ((err = putzoneent(&ze, PZE_MODIFY)) != Z_OK) + return (err); + + return (Z_OK); +} + +/* * File-system convenience functions. */ boolean_t @@ -7000,86 +7567,49 @@ zonecfg_getpsetent(zone_dochandle_t handle, struct zone_psettab *tabptr) return (err); } -static int -add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr) -{ - xmlNodePtr newnode, cur = handle->zone_dh_cur; - int err; - - newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL); - if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap)) - != Z_OK) - return (err); - - return (Z_OK); -} - +/* + * Cleanup obsolete constructs in the configuration. + * Return true of the config has been updated and must be commited. + */ int -zonecfg_delete_mcap(zone_dochandle_t handle) +zonecfg_fix_obsolete(zone_dochandle_t handle) { - int err; - xmlNodePtr cur = handle->zone_dh_cur; + int res = 0; + int add_physmem_rctl = 0; + xmlNodePtr cur; + char zone_physmem_cap[MAXNAMELEN]; - if ((err = operation_prep(handle)) != Z_OK) - return (err); + if (operation_prep(handle) != Z_OK) + return (res); + /* + * If an obsolete mcap entry exists, convert it to the rctl. + */ + cur = handle->zone_dh_cur; for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0) continue; + if (fetchprop(cur, DTD_ATTR_PHYSCAP, + zone_physmem_cap, sizeof (zone_physmem_cap)) == Z_OK) { + res = 1; + add_physmem_rctl = 1; + } + xmlUnlinkNode(cur); xmlFreeNode(cur); - return (Z_OK); + break; } - return (Z_NO_RESOURCE_ID); -} - -int -zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr) -{ - int err; - if (tabptr == NULL) - return (Z_INVAL); - - err = zonecfg_delete_mcap(handle); - /* it is ok if there is no mcap entry */ - if (err != Z_OK && err != Z_NO_RESOURCE_ID) - return (err); - - if ((err = add_mcap(handle, tabptr)) != Z_OK) - return (err); - - return (Z_OK); -} + if (add_physmem_rctl) { + uint64_t cap; + char *endp; -int -zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr) -{ - xmlNodePtr cur; - int err; - - if (tabptr == NULL) - return (Z_INVAL); - - if ((err = operation_prep(handle)) != Z_OK) - return (err); - - cur = handle->zone_dh_cur; - for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { - if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0) - continue; - if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, - tabptr->zone_physmem_cap, - sizeof (tabptr->zone_physmem_cap))) != Z_OK) { - handle->zone_dh_cur = handle->zone_dh_top; - return (err); - } - - return (Z_OK); + cap = strtoull(zone_physmem_cap, &endp, 10); + (void) zonecfg_set_aliased_rctl(handle, ALIAS_MAXPHYSMEM, cap); } - return (Z_NO_ENTRY); + return (res); } int @@ -7137,51 +7667,6 @@ zonecfg_getsecflagsent(zone_dochandle_t handle, return (err); } -static int -getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr) -{ - xmlNodePtr cur; - int err; - - if (handle == NULL) - return (Z_INVAL); - - if ((cur = handle->zone_dh_cur) == NULL) - return (Z_NO_ENTRY); - - for (; cur != NULL; cur = cur->next) - if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0) - break; - if (cur == NULL) { - handle->zone_dh_cur = handle->zone_dh_top; - return (Z_NO_ENTRY); - } - - if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap, - sizeof (tabptr->zone_physmem_cap))) != Z_OK) { - handle->zone_dh_cur = handle->zone_dh_top; - return (err); - } - - handle->zone_dh_cur = cur->next; - return (Z_OK); -} - -int -zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr) -{ - int err; - - if ((err = zonecfg_setent(handle)) != Z_OK) - return (err); - - err = getmcapent_core(handle, tabptr); - - (void) zonecfg_endent(handle); - - return (err); -} - /* * Get the full tree of pkg metadata in a set of nested AVL trees. * pkgs_avl is an AVL tree of pkgs. @@ -7847,7 +8332,7 @@ zonecfg_update_userauths(zone_dochandle_t handle, char *zonename) (void) fclose(uaf); return (Z_MISC_FS); } - if (!config_file_path(zonename, config_file)) { + if (!config_file_path(zonename, config_file, sizeof (config_file))) { (void) fclose(uaf); return (Z_MISC_FS); } diff --git a/usr/src/lib/libzonecfg/common/mapfile-vers b/usr/src/lib/libzonecfg/common/mapfile-vers index c73533b97f..da4009d2fa 100644 --- a/usr/src/lib/libzonecfg/common/mapfile-vers +++ b/usr/src/lib/libzonecfg/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015, Joyent Inc. # # @@ -53,6 +54,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_add_fs_option; zonecfg_add_admin; zonecfg_add_nwif; + zonecfg_add_res_attr; zonecfg_add_pkg; zonecfg_add_pset; zonecfg_add_rctl; @@ -80,7 +82,6 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_delete_dev; zonecfg_delete_ds; zonecfg_delete_filesystem; - zonecfg_delete_mcap; zonecfg_delete_nwif; zonecfg_delete_pset; zonecfg_delete_rctl; @@ -106,7 +107,9 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_find_mounts; zonecfg_find_scratch; zonecfg_fini_handle; + zonecfg_fix_obsolete; zonecfg_free_fs_option_list; + zonecfg_free_res_attr_list; zonecfg_free_rctl_value_list; zonecfg_get_aliased_rctl; zonecfg_get_attach_handle; @@ -120,6 +123,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_get_bootargs; zonecfg_get_brand; zonecfg_get_dflt_sched_class; + zonecfg_get_did; zonecfg_getdevent; zonecfg_getdevperment; zonecfg_getdsent; @@ -129,7 +133,6 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_get_hostid; zonecfg_get_iptype; zonecfg_get_limitpriv; - zonecfg_getmcapent; zonecfg_get_name; zonecfg_get_name_by_uuid; zonecfg_getnwifent; @@ -163,7 +166,6 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_lookup_dev; zonecfg_lookup_ds; zonecfg_lookup_filesystem; - zonecfg_lookup_mcap; zonecfg_lookup_nwif; zonecfg_lookup_pset; zonecfg_lookup_rctl; @@ -173,12 +175,12 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_modify_dev; zonecfg_modify_ds; zonecfg_modify_filesystem; - zonecfg_modify_mcap; zonecfg_modify_nwif; zonecfg_modify_pset; zonecfg_modify_rctl; zonecfg_modify_secflags; zonecfg_notify_bind; + zonecfg_notify_create; zonecfg_notify_critical_abort; zonecfg_notify_critical_enter; zonecfg_notify_critical_exit; @@ -188,6 +190,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_ping_zoneadmd; zonecfg_release_lock_file; zonecfg_remove_fs_option; + zonecfg_remove_res_attr; zonecfg_remove_rctl_value; zonecfg_remove_userauths; zonecfg_reverse_scratch; @@ -201,6 +204,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_set_autoboot; zonecfg_set_bootargs; zonecfg_set_brand; + zonecfg_set_did; zonecfg_setdevent; zonecfg_setdevperment; zonecfg_setdsent; @@ -216,6 +220,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_set_root; zonecfg_set_sched; zonecfg_set_swinv; + zonecfg_set_uuid; zonecfg_set_zonepath; zonecfg_strerror; zonecfg_str_to_bytes; @@ -234,6 +239,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_verify_save; zonecfg_warn_poold; zone_get_brand; + zone_get_did; zone_get_devroot; zone_get_id; zone_get_rootpath; diff --git a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 index 03be1a2bf5..228bb8ace2 100644 --- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 +++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 @@ -21,6 +21,7 @@ CDDL HEADER END Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2011, Joyent Inc. All rights reserved. --> @@ -46,14 +47,21 @@ <!ATTLIST inherited-pkg-dir directory CDATA #REQUIRED> -<!ELEMENT network EMPTY> +<!ELEMENT net-attr EMPTY> +<!ATTLIST net-attr name CDATA #REQUIRED + value CDATA #REQUIRED> + +<!ELEMENT network (net-attr)*> <!ATTLIST network address CDATA "" allowed-address CDATA "" defrouter CDATA "" - physical CDATA #REQUIRED> + global-nic CDATA "" + mac-addr CDATA "" + physical CDATA #REQUIRED + vlan-id CDATA ""> -<!ELEMENT device EMPTY> +<!ELEMENT device (net-attr)*> <!ATTLIST device match CDATA #REQUIRED> @@ -162,6 +170,7 @@ limitpriv CDATA "" bootargs CDATA "" brand CDATA "" + debugid CDATA "" scheduling-class CDATA "" fs-allowed CDATA "" version NMTOKEN #FIXED '1'> diff --git a/usr/src/lib/libzpool/Makefile.com b/usr/src/lib/libzpool/Makefile.com index d52d56b1ff..31bd6aa5e2 100644 --- a/usr/src/lib/libzpool/Makefile.com +++ b/usr/src/lib/libzpool/Makefile.com @@ -75,6 +75,11 @@ CPPFLAGS.first = -I$(SRC)/lib/libfakekernel/common CPPFLAGS += $(INCS) -DDEBUG -D_FAKE_KERNEL +# The following is needed to fix the SmartOS build; see OS-6582. We cannot do +# a conditional appendage to INCS, since that breaks the lint build. +CFLAGS += -isystem $(ROOT)/usr/include +CFLAGS64 += -isystem $(ROOT)/usr/include + CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-switch CERRWARN += -_gcc=-Wno-type-limits diff --git a/usr/src/lib/nsswitch/dns/Makefile.com b/usr/src/lib/nsswitch/dns/Makefile.com index e1a02743d1..bc66038274 100644 --- a/usr/src/lib/nsswitch/dns/Makefile.com +++ b/usr/src/lib/nsswitch/dns/Makefile.com @@ -52,5 +52,5 @@ $(ROOT)/usr/lib/$(MACH64)/$(DYNLIB1):= \ CPPFLAGS += -DNSS_DNS_LIBRESOLV=\"libresolv.so.2\" -LDLIBS += -lnsl -lsocket - +LDLIBS += -lnsl -lresolv_joy -lsocket +DYNLIB1 = nss_dns.so$(VERS) diff --git a/usr/src/lib/nsswitch/dns/common/dns_common.h b/usr/src/lib/nsswitch/dns/common/dns_common.h index 717f56d70f..83d486cf57 100644 --- a/usr/src/lib/nsswitch/dns/common/dns_common.h +++ b/usr/src/lib/nsswitch/dns/common/dns_common.h @@ -31,8 +31,6 @@ #ifndef _DNS_COMMON_H #define _DNS_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <ctype.h> #include <sys/types.h> @@ -43,7 +41,7 @@ #include <thread.h> #include <arpa/inet.h> #include <arpa/nameser.h> -#include <resolv.h> +#include <resolv_joy.h> #include <syslog.h> #include <nsswitch.h> #include <nss_common.h> diff --git a/usr/src/lib/nsswitch/dns/common/dns_mt.c b/usr/src/lib/nsswitch/dns/common/dns_mt.c index 128b1bde75..4af3f671c0 100644 --- a/usr/src/lib/nsswitch/dns/common/dns_mt.c +++ b/usr/src/lib/nsswitch/dns/common/dns_mt.c @@ -23,8 +23,9 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * dns_mt.c @@ -49,52 +50,41 @@ static void _nss_dns_init(void); extern struct hostent *res_gethostbyname(const char *); -#pragma weak res_gethostbyname -#define RES_SET_NO_HOSTS_FALLBACK "__res_set_no_hosts_fallback" -extern void __res_set_no_hosts_fallback(void); -#pragma weak __res_set_no_hosts_fallback +#define RES_SET_NO_HOSTS_FALLBACK "__joy_res_set_no_hosts_fallback" +extern void __joy_res_set_no_hosts_fallback(void); -#define RES_UNSET_NO_HOSTS_FALLBACK "__res_unset_no_hosts_fallback" -extern void __res_unset_no_hosts_fallback(void); -#pragma weak __res_unset_no_hosts_fallback +#define RES_UNSET_NO_HOSTS_FALLBACK "__joy_res_unset_no_hosts_fallback" +extern void __joy_res_unset_no_hosts_fallback(void); #define RES_GET_RES "__res_get_res" extern struct __res_state *__res_get_res(void); -#pragma weak __res_get_res #define RES_ENABLE_MT "__res_enable_mt" extern int __res_enable_mt(void); -#pragma weak __res_enable_mt #define RES_DISABLE_MT "__res_disable_mt" extern int __res_disable_mt(void); -#pragma weak __res_disable_mt #define RES_GET_H_ERRNO "__res_get_h_errno" extern int *__res_get_h_errno(); -#pragma weak __res_get_h_errno -#define __H_ERRNO "__h_errno" -extern int *__h_errno(void); -#pragma weak __h_errno +#define __H_ERRNO "__joy_h_errno" +extern int *__joy_h_errno(void); -#define RES_OVERRIDE_RETRY "__res_override_retry" -extern int __res_override_retry(int); -#pragma weak __res_override_retry +#define RES_OVERRIDE_RETRY "__joy_res_override_retry" +extern int __joy_res_override_retry(int); static void __fallback_set_no_hosts(void); -static int *__fallback_h_errno(void); -static int __fallback_override_retry(int); static int __is_mt_safe(void); void (*set_no_hosts_fallback)(void) = __fallback_set_no_hosts; void (*unset_no_hosts_fallback)(void) = __fallback_set_no_hosts; struct __res_state *(*set_res_retry)() = 0; -int (*enable_mt)() = 0; -int (*disable_mt)() = 0; -int *(*get_h_errno)(void) = 0; -int (*override_retry)(int) = 0; +int (*enable_mt)() = __is_mt_safe; +int (*disable_mt)() = __is_mt_safe; +int *(*get_h_errno)(void) = __joy_h_errno; +int (*override_retry)(int) = __joy_res_override_retry; /* Usually set from the Makefile */ #ifndef NSS_DNS_LIBRESOLV @@ -106,91 +96,12 @@ extern int h_errno; mutex_t one_lane = DEFAULTMUTEX; +/* Because we link against libresolv_joy.so.2, this is relatively easy. */ void _nss_dns_init(void) { - void *reslib, (*f_void_ptr)(); - - /* If no libresolv library, then load one */ - if (res_gethostbyname == 0) { - if ((reslib = - dlopen(NSS_DNS_LIBRESOLV, RTLD_LAZY|RTLD_GLOBAL)) != 0) { - /* Turn off /etc/hosts fall back in libresolv */ - if ((f_void_ptr = (void (*)(void))dlsym(reslib, - RES_SET_NO_HOSTS_FALLBACK)) != 0) { - set_no_hosts_fallback = f_void_ptr; - } - if ((f_void_ptr = (void (*)(void))dlsym(reslib, - RES_SET_NO_HOSTS_FALLBACK)) != 0) { - unset_no_hosts_fallback = f_void_ptr; - } - /* Set number of resolver retries */ - if ((override_retry = (int (*)(int))dlsym(reslib, - RES_OVERRIDE_RETRY)) == 0) { - set_res_retry = - (struct __res_state *(*)(void))dlsym(reslib, - RES_GET_RES); - override_retry = __fallback_override_retry; - } - /* - * Select h_errno retrieval function. A BIND 8.2.2 - * libresolv.so.2 will have __h_errno, a BIND 8.1.2 - * one will have __res_get_h_errno, and other - * versions may have nothing at all. - * - * Also try to bind to the relevant MT enable/disable - * functions which are also dependent on the version - * of the BIND libresolv.so.2 being used. - */ - if ((get_h_errno = (int *(*)(void))dlsym(reslib, - __H_ERRNO)) != 0) { - /* BIND 8.2.2 libresolv.so.2 is MT safe. */ - enable_mt = __is_mt_safe; - disable_mt = __is_mt_safe; - } else { - if ((get_h_errno = - (int *(*)(void))dlsym(reslib, - RES_GET_H_ERRNO)) == 0) { - get_h_errno = __fallback_h_errno; - } - /* - * Pre-BIND 8.2.2 was not MT safe. Try to - * bind the MT enable/disable functions. - */ - if ((enable_mt = (int (*)(void))dlsym(reslib, - RES_ENABLE_MT)) != 0 && - (disable_mt = (int (*)(void))dlsym(reslib, - RES_DISABLE_MT)) == 0) { - enable_mt = 0; - } - } - } - } else { - /* Libresolv already loaded */ - if ((f_void_ptr = __res_set_no_hosts_fallback) != 0) { - set_no_hosts_fallback = f_void_ptr; - } - if ((f_void_ptr = __res_unset_no_hosts_fallback) != 0) { - unset_no_hosts_fallback = f_void_ptr; - } - if ((override_retry = __res_override_retry) == 0) { - set_res_retry = __res_get_res; - override_retry = __fallback_override_retry; - } - if ((get_h_errno = __h_errno) == 0 && - (get_h_errno = __res_get_h_errno) == 0) { - get_h_errno = __fallback_h_errno; - } - if (get_h_errno == __h_errno) { - enable_mt = __is_mt_safe; - disable_mt = __is_mt_safe; - } else { - if ((enable_mt = __res_enable_mt) != 0 && - (disable_mt = __res_disable_mt) == 0) { - enable_mt = 0; - } - } - } + enable_mt = __is_mt_safe; + disable_mt = __is_mt_safe; } @@ -228,36 +139,6 @@ __is_mt_safe(void) { } -/* - * Return pointer to the global h_errno variable - */ -static int * -__fallback_h_errno(void) { - return (&h_errno); -} - - -/* - * This function is called when the resolver library doesn't provide its - * own function to establish an override retry. If we can get a pointer - * to the per-thread _res (i.e., set_res_retry != 0), we set the retries - * directly, and return the previous number of retries. Otherwise, there's - * nothing to do. - */ -static int -__fallback_override_retry(int retry) { - struct __res_state *res; - int old_retry = 0; - - if (set_res_retry != 0) { - res = set_res_retry(); - old_retry = res->retry; - res->retry = retry; - } - return (old_retry); -} - - static void __fallback_set_no_hosts(void) { } diff --git a/usr/src/lib/nsswitch/dns/common/gethostent.c b/usr/src/lib/nsswitch/dns/common/gethostent.c index 648ea8ba01..d321dd24c6 100644 --- a/usr/src/lib/nsswitch/dns/common/gethostent.c +++ b/usr/src/lib/nsswitch/dns/common/gethostent.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * gethostent.c * @@ -53,12 +51,6 @@ static struct hostent *_gethostbyaddr(int *h_errnop, const char *addr, int len, int type); struct hostent *_nss_dns_gethostbyname2(int *h_errnop, const char *name); -#pragma weak res_gethostbyname -#pragma weak res_gethostbyname2 -#pragma weak res_gethostbyaddr -#pragma weak res_sethostent -#pragma weak res_endhostent - nss_backend_t *_nss_dns_constr(dns_backend_op_t ops[], int n_ops); nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *); diff --git a/usr/src/lib/nsswitch/dns/common/gethostent6.c b/usr/src/lib/nsswitch/dns/common/gethostent6.c index ee85832073..f3efc4eae6 100644 --- a/usr/src/lib/nsswitch/dns/common/gethostent6.c +++ b/usr/src/lib/nsswitch/dns/common/gethostent6.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This is the DNS backend for IPv6 addresses. * getbyname() is a local routine, but getbyaddr() actually shares the @@ -50,8 +48,6 @@ */ -#pragma weak res_endhostent - extern struct hostent *_gethostbyname(int *, const char *); extern struct hostent *_nss_dns_gethostbyname2(int *, const char *); diff --git a/usr/src/lib/pysolaris/Makefile.com b/usr/src/lib/pysolaris/Makefile.com index 4a65fe3976..ac9b344e40 100644 --- a/usr/src/lib/pysolaris/Makefile.com +++ b/usr/src/lib/pysolaris/Makefile.com @@ -45,6 +45,7 @@ LIBS = $(DYNLIB) LDLIBS += -lc -lsec -lidmap -lpython$(PYVER)$(PYSUFFIX) NATIVE_LIBS += libpython$(PYVER)$(PYSUFFIX).so CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include/python2.6 CERRWARN += -_gcc=-Wno-unused-variable CPPFLAGS += \ -I$(ADJUNCT_PROTO)/usr/include/python$(PYVER)$(PYSUFFIX) diff --git a/usr/src/lib/varpd/Makefile b/usr/src/lib/varpd/Makefile new file mode 100644 index 0000000000..a0e4537ab9 --- /dev/null +++ b/usr/src/lib/varpd/Makefile @@ -0,0 +1,34 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +SUBDIRS = libvarpd direct files svp + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +check := TARGET = check +install := TARGET = install +install_h := TARGET = install_h +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install install_h check lint: $(SUBDIRS) +direct files svp: libvarpd + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/varpd/Makefile.plugin b/usr/src/lib/varpd/Makefile.plugin new file mode 100644 index 0000000000..48f188500c --- /dev/null +++ b/usr/src/lib/varpd/Makefile.plugin @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +ROOTLIBDIR = $(ROOT)/usr/lib/varpd +ROOTLIBDIR64 = $(ROOT)/usr/lib/varpd/$(MACH64) + +MAPFILES += ../../libvarpd/common/mapfile-plugin diff --git a/usr/src/lib/varpd/direct/Makefile b/usr/src/lib/varpd/direct/Makefile new file mode 100644 index 0000000000..275f07bf8b --- /dev/null +++ b/usr/src/lib/varpd/direct/Makefile @@ -0,0 +1,40 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../../Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: + +check: + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ diff --git a/usr/src/lib/varpd/direct/Makefile.com b/usr/src/lib/varpd/direct/Makefile.com new file mode 100644 index 0000000000..c9baf53cd4 --- /dev/null +++ b/usr/src/lib/varpd/direct/Makefile.com @@ -0,0 +1,38 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY = libvarpd_direct.a +VERS = .1 +OBJECTS = libvarpd_direct.o + +include ../../../Makefile.lib +include ../../Makefile.plugin + +LIBS = $(DYNLIB) +LDLIBS += -lc -lumem -lnvpair +CPPFLAGS += -I../common + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +SRCDIR = ../common + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../Makefile.targ diff --git a/usr/src/lib/varpd/direct/amd64/Makefile b/usr/src/lib/varpd/direct/amd64/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/direct/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/direct/common/libvarpd_direct.c b/usr/src/lib/varpd/direct/common/libvarpd_direct.c new file mode 100644 index 0000000000..018cdf641c --- /dev/null +++ b/usr/src/lib/varpd/direct/common/libvarpd_direct.c @@ -0,0 +1,411 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +/* + * Point to point plug-in for varpd. + * + * This plugin implements a simple point to point plugin for a packet. It + * represents the traditional tunnel, just in overlay form. As such, the only + * properties it needs are those to determine where to send everything. At this + * time, we don't allow a mulicast address; however, there's no reason that the + * direct plugin shouldn't in theory support multicast, though when implementing + * it the best path will become clear. + * + * In general this module has been designed to make it easy to support a + * destination of either IP or IP and port; however, we restrict it to the + * latter as we don't currently have an implementation that would allow us to + * test that. + */ + +#include <libvarpd_provider.h> +#include <umem.h> +#include <errno.h> +#include <thread.h> +#include <synch.h> +#include <strings.h> +#include <assert.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <libnvpair.h> + +typedef struct varpd_direct { + overlay_plugin_dest_t vad_dest; /* RO */ + mutex_t vad_lock; /* Protects the rest */ + boolean_t vad_hip; + boolean_t vad_hport; + struct in6_addr vad_ip; + uint16_t vad_port; +} varpd_direct_t; + +static const char *varpd_direct_props[] = { + "direct/dest_ip", + "direct/dest_port" +}; + +static boolean_t +varpd_direct_valid_dest(overlay_plugin_dest_t dest) +{ + if (dest & ~(OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT)) + return (B_FALSE); + + if (!(dest & (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))) + return (B_FALSE); + + return (B_TRUE); +} + +/* ARGSUSED */ +static int +varpd_direct_create(varpd_provider_handle_t *hdl, void **outp, + overlay_plugin_dest_t dest) +{ + int ret; + varpd_direct_t *vdp; + + if (varpd_direct_valid_dest(dest) == B_FALSE) + return (ENOTSUP); + + vdp = umem_alloc(sizeof (varpd_direct_t), UMEM_DEFAULT); + if (vdp == NULL) + return (ENOMEM); + + if ((ret = mutex_init(&vdp->vad_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL)) != 0) { + umem_free(vdp, sizeof (varpd_direct_t)); + return (ret); + } + + vdp->vad_dest = dest; + vdp->vad_hip = B_FALSE; + vdp->vad_hport = B_FALSE; + *outp = vdp; + return (0); +} + +static int +varpd_direct_start(void *arg) +{ + varpd_direct_t *vdp = arg; + + mutex_enter(&vdp->vad_lock); + if (vdp->vad_hip == B_FALSE ||((vdp->vad_dest & OVERLAY_PLUGIN_D_IP) && + vdp->vad_hport == B_FALSE)) { + mutex_exit(&vdp->vad_lock); + return (EAGAIN); + } + mutex_exit(&vdp->vad_lock); + + return (0); +} + +/* ARGSUSED */ +static void +varpd_direct_stop(void *arg) +{ +} + +static void +varpd_direct_destroy(void *arg) +{ + varpd_direct_t *vdp = arg; + + if (mutex_destroy(&vdp->vad_lock) != 0) + abort(); + umem_free(vdp, sizeof (varpd_direct_t)); +} + +static int +varpd_direct_default(void *arg, overlay_target_point_t *otp) +{ + varpd_direct_t *vdp = arg; + + mutex_enter(&vdp->vad_lock); + bcopy(&vdp->vad_ip, &otp->otp_ip, sizeof (struct in6_addr)); + otp->otp_port = vdp->vad_port; + mutex_exit(&vdp->vad_lock); + + return (VARPD_LOOKUP_OK); +} + +static int +varpd_direct_nprops(void *arg, uint_t *nprops) +{ + const varpd_direct_t *vdp = arg; + + *nprops = 0; + if (vdp->vad_dest & OVERLAY_PLUGIN_D_ETHERNET) + *nprops += 1; + + if (vdp->vad_dest & OVERLAY_PLUGIN_D_IP) + *nprops += 1; + + if (vdp->vad_dest & OVERLAY_PLUGIN_D_PORT) + *nprops += 1; + + assert(*nprops == 1 || *nprops == 2); + + return (0); +} + +static int +varpd_direct_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph) +{ + varpd_direct_t *vdp = arg; + + /* + * Because we only support IP + port combos right now, prop 0 should + * always be the IP. We don't support a port without an IP. + */ + assert(vdp->vad_dest & OVERLAY_PLUGIN_D_IP); + if (propid == 0) { + libvarpd_prop_set_name(vph, varpd_direct_props[0]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_IP); + libvarpd_prop_set_nodefault(vph); + return (0); + } + + if (propid == 1 && vdp->vad_dest & OVERLAY_PLUGIN_D_PORT) { + libvarpd_prop_set_name(vph, varpd_direct_props[1]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_UINT); + libvarpd_prop_set_nodefault(vph); + libvarpd_prop_set_range_uint32(vph, 1, UINT16_MAX); + return (0); + } + + return (EINVAL); +} + +static int +varpd_direct_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep) +{ + varpd_direct_t *vdp = arg; + + /* direct/dest_ip */ + if (strcmp(pname, varpd_direct_props[0]) == 0) { + if (*sizep < sizeof (struct in6_addr)) + return (EOVERFLOW); + mutex_enter(&vdp->vad_lock); + if (vdp->vad_hip == B_FALSE) { + *sizep = 0; + } else { + bcopy(&vdp->vad_ip, buf, sizeof (struct in6_addr)); + *sizep = sizeof (struct in6_addr); + } + mutex_exit(&vdp->vad_lock); + return (0); + } + + /* direct/dest_port */ + if (strcmp(pname, varpd_direct_props[1]) == 0) { + uint64_t val; + + if (*sizep < sizeof (uint64_t)) + return (EOVERFLOW); + mutex_enter(&vdp->vad_lock); + if (vdp->vad_hport == B_FALSE) { + *sizep = 0; + } else { + val = vdp->vad_port; + bcopy(&val, buf, sizeof (uint64_t)); + *sizep = sizeof (uint64_t); + } + mutex_exit(&vdp->vad_lock); + return (0); + } + + return (EINVAL); +} + +static int +varpd_direct_setprop(void *arg, const char *pname, const void *buf, + const uint32_t size) +{ + varpd_direct_t *vdp = arg; + + /* direct/dest_ip */ + if (strcmp(pname, varpd_direct_props[0]) == 0) { + const struct in6_addr *ipv6 = buf; + + if (size < sizeof (struct in6_addr)) + return (EOVERFLOW); + + if (IN6_IS_ADDR_V4COMPAT(ipv6)) + return (EINVAL); + + if (IN6_IS_ADDR_6TO4(ipv6)) + return (EINVAL); + + mutex_enter(&vdp->vad_lock); + bcopy(buf, &vdp->vad_ip, sizeof (struct in6_addr)); + vdp->vad_hip = B_TRUE; + mutex_exit(&vdp->vad_lock); + return (0); + } + + /* direct/dest_port */ + if (strcmp(pname, varpd_direct_props[1]) == 0) { + const uint64_t *valp = buf; + if (size < sizeof (uint64_t)) + return (EOVERFLOW); + + if (*valp == 0 || *valp > UINT16_MAX) + return (EINVAL); + + mutex_enter(&vdp->vad_lock); + vdp->vad_port = (uint16_t)*valp; + vdp->vad_hport = B_TRUE; + mutex_exit(&vdp->vad_lock); + return (0); + } + + return (EINVAL); +} + +static int +varpd_direct_save(void *arg, nvlist_t *nvp) +{ + int ret; + varpd_direct_t *vdp = arg; + + mutex_enter(&vdp->vad_lock); + if (vdp->vad_hport == B_TRUE) { + if ((ret = nvlist_add_uint16(nvp, varpd_direct_props[1], + vdp->vad_port)) != 0) { + mutex_exit(&vdp->vad_lock); + return (ret); + } + } + + if (vdp->vad_hip == B_TRUE) { + char buf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, &vdp->vad_ip, buf, sizeof (buf)) == + NULL) + abort(); + if ((ret = nvlist_add_string(nvp, varpd_direct_props[0], + buf)) != 0) { + mutex_exit(&vdp->vad_lock); + return (ret); + } + } + mutex_exit(&vdp->vad_lock); + + return (0); +} + +/* ARGSUSED */ +static int +varpd_direct_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl, + overlay_plugin_dest_t dest, void **outp) +{ + int ret; + char *ipstr; + varpd_direct_t *vdp; + + if (varpd_direct_valid_dest(dest) == B_FALSE) + return (ENOTSUP); + + vdp = umem_alloc(sizeof (varpd_direct_t), UMEM_DEFAULT); + if (vdp == NULL) + return (ENOMEM); + + if ((ret = mutex_init(&vdp->vad_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL)) != 0) { + umem_free(vdp, sizeof (varpd_direct_t)); + return (ret); + } + + if ((ret = nvlist_lookup_uint16(nvp, varpd_direct_props[1], + &vdp->vad_port)) != 0) { + if (ret != ENOENT) { + if (mutex_destroy(&vdp->vad_lock) != 0) + abort(); + umem_free(vdp, sizeof (varpd_direct_t)); + return (ret); + } + vdp->vad_hport = B_FALSE; + } else { + vdp->vad_hport = B_TRUE; + } + + if ((ret = nvlist_lookup_string(nvp, varpd_direct_props[0], + &ipstr)) != 0) { + if (ret != ENOENT) { + if (mutex_destroy(&vdp->vad_lock) != 0) + abort(); + umem_free(vdp, sizeof (varpd_direct_t)); + return (ret); + } + vdp->vad_hip = B_FALSE; + } else { + ret = inet_pton(AF_INET6, ipstr, &vdp->vad_ip); + /* + * inet_pton is only defined to return -1 with errno set to + * EAFNOSUPPORT, which really, shouldn't happen. + */ + if (ret == -1) { + assert(errno == EAFNOSUPPORT); + abort(); + } + if (ret == 0) { + if (mutex_destroy(&vdp->vad_lock) != 0) + abort(); + umem_free(vdp, sizeof (varpd_direct_t)); + return (EINVAL); + } + } + + *outp = vdp; + return (0); +} + +static const varpd_plugin_ops_t varpd_direct_ops = { + 0, + varpd_direct_create, + varpd_direct_start, + varpd_direct_stop, + varpd_direct_destroy, + varpd_direct_default, + NULL, + varpd_direct_nprops, + varpd_direct_propinfo, + varpd_direct_getprop, + varpd_direct_setprop, + varpd_direct_save, + varpd_direct_restore +}; + +#pragma init(varpd_direct_init) +static void +varpd_direct_init(void) +{ + int err; + varpd_plugin_register_t *vpr; + + vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err); + if (vpr == NULL) + return; + + vpr->vpr_mode = OVERLAY_TARGET_POINT; + vpr->vpr_name = "direct"; + vpr->vpr_ops = &varpd_direct_ops; + (void) libvarpd_plugin_register(vpr); + libvarpd_plugin_free(vpr); +} diff --git a/usr/src/lib/varpd/direct/common/llib-lvarpd_direct b/usr/src/lib/varpd/direct/common/llib-lvarpd_direct new file mode 100644 index 0000000000..03c34f4fcb --- /dev/null +++ b/usr/src/lib/varpd/direct/common/llib-lvarpd_direct @@ -0,0 +1,18 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + diff --git a/usr/src/lib/varpd/direct/common/mapfile-vers b/usr/src/lib/varpd/direct/common/mapfile-vers new file mode 100644 index 0000000000..6b7c5a5067 --- /dev/null +++ b/usr/src/lib/varpd/direct/common/mapfile-vers @@ -0,0 +1,35 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate { + local: + *; +}; diff --git a/usr/src/lib/varpd/direct/i386/Makefile b/usr/src/lib/varpd/direct/i386/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/direct/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/direct/sparc/Makefile b/usr/src/lib/varpd/direct/sparc/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/direct/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/direct/sparcv9/Makefile b/usr/src/lib/varpd/direct/sparcv9/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/direct/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/files/Makefile b/usr/src/lib/varpd/files/Makefile new file mode 100644 index 0000000000..275f07bf8b --- /dev/null +++ b/usr/src/lib/varpd/files/Makefile @@ -0,0 +1,40 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../../Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install_h: + +check: + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ diff --git a/usr/src/lib/varpd/files/Makefile.com b/usr/src/lib/varpd/files/Makefile.com new file mode 100644 index 0000000000..dd8009d002 --- /dev/null +++ b/usr/src/lib/varpd/files/Makefile.com @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY = libvarpd_files.a +VERS = .1 +OBJECTS = libvarpd_files.o \ + libvarpd_files_json.o + +include ../../../Makefile.lib +include ../../Makefile.plugin + +LIBS = $(DYNLIB) +LDLIBS += -lc -lumem -lnvpair -lsocket -lcustr +CPPFLAGS += -I../common + +LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN +LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +SRCDIR = ../common + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../Makefile.targ diff --git a/usr/src/lib/varpd/files/amd64/Makefile b/usr/src/lib/varpd/files/amd64/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/files/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/files/common/libvarpd_files.c b/usr/src/lib/varpd/files/common/libvarpd_files.c new file mode 100644 index 0000000000..812919a07d --- /dev/null +++ b/usr/src/lib/varpd/files/common/libvarpd_files.c @@ -0,0 +1,605 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + +/* + * Files based plug-in for varpd + * + * This is a dynamic varpd plug-in that has a static backing store. It's really + * nothing more than a glorified version of /etc/ethers, though it facilitiates + * a bit more. The files module allows for the full set of mappings to be fixed + * at creation time. In addition, it also provides support for proxying ARP, + * NDP, and DHCP. + * + * At this time, the plugin requires that the destination type involve both an + * IP address and a port; however, there's no reason that this cannot be made + * more flexible as we have additional encapsulation algorithms that support it. + * The plug-in only has a single property, which is the location of the JSON + * file. The JSON file itself looks something like: + * + * { + * "aa:bb:cc:dd:ee:ff": { + * "arp": "10.23.69.1", + * "ndp": "2600:3c00::f03c:91ff:fe96:a264", + * "ip": "192.168.1.1", + * "port": 8080 + * }, + * ... + * } + */ + +#include <libvarpd_provider.h> +#include <umem.h> +#include <errno.h> +#include <thread.h> +#include <synch.h> +#include <strings.h> +#include <assert.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libnvpair.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/ethernet.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <libvarpd_files_json.h> + +typedef struct varpd_files { + overlay_plugin_dest_t vaf_dest; /* RO */ + varpd_provider_handle_t *vaf_hdl; /* RO */ + char *vaf_path; /* WO */ + nvlist_t *vaf_nvl; /* WO */ + uint64_t vaf_nmisses; /* Atomic */ + uint64_t vaf_narp; /* Atomic */ +} varpd_files_t; + +static const char *varpd_files_props[] = { + "files/config" +}; + +static boolean_t +varpd_files_valid_dest(overlay_plugin_dest_t dest) +{ + if (dest & ~(OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT)) + return (B_FALSE); + + if (!(dest & (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))) + return (B_FALSE); + + return (B_TRUE); +} + +static int +varpd_files_create(varpd_provider_handle_t *hdl, void **outp, + overlay_plugin_dest_t dest) +{ + varpd_files_t *vaf; + + if (varpd_files_valid_dest(dest) == B_FALSE) + return (ENOTSUP); + + vaf = umem_alloc(sizeof (varpd_files_t), UMEM_DEFAULT); + if (vaf == NULL) + return (ENOMEM); + + bzero(vaf, sizeof (varpd_files_t)); + vaf->vaf_dest = dest; + vaf->vaf_path = NULL; + vaf->vaf_nvl = NULL; + vaf->vaf_hdl = hdl; + *outp = vaf; + return (0); +} + +static int +varpd_files_normalize_nvlist(varpd_files_t *vaf, nvlist_t *nvl) +{ + int ret; + nvlist_t *out; + nvpair_t *pair; + + if ((ret = nvlist_alloc(&out, NV_UNIQUE_NAME, 0)) != 0) + return (ret); + + for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL; + pair = nvlist_next_nvpair(nvl, pair)) { + char *name, fname[ETHERADDRSTRL]; + nvlist_t *data; + struct ether_addr ether, *e; + e = ðer; + + if (nvpair_type(pair) != DATA_TYPE_NVLIST) { + nvlist_free(out); + return (EINVAL); + } + + name = nvpair_name(pair); + if ((ret = nvpair_value_nvlist(pair, &data)) != 0) { + nvlist_free(out); + return (EINVAL); + } + + if (ether_aton_r(name, e) == NULL) { + nvlist_free(out); + return (EINVAL); + } + + if (ether_ntoa_r(e, fname) == NULL) { + nvlist_free(out); + return (ENOMEM); + } + + if ((ret = nvlist_add_nvlist(out, fname, data)) != 0) { + nvlist_free(out); + return (EINVAL); + } + } + + vaf->vaf_nvl = out; + return (0); +} + +static int +varpd_files_start(void *arg) +{ + int fd, ret; + void *maddr; + struct stat st; + nvlist_t *nvl; + varpd_files_t *vaf = arg; + + if (vaf->vaf_path == NULL) + return (EAGAIN); + + if ((fd = open(vaf->vaf_path, O_RDONLY)) < 0) + return (errno); + + if (fstat(fd, &st) != 0) { + ret = errno; + if (close(fd) != 0) + abort(); + return (ret); + } + + maddr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + if (maddr == NULL) { + ret = errno; + if (close(fd) != 0) + abort(); + return (ret); + } + + ret = nvlist_parse_json(maddr, st.st_size, &nvl, + NVJSON_FORCE_INTEGER, NULL); + if (ret == 0) { + ret = varpd_files_normalize_nvlist(vaf, nvl); + nvlist_free(nvl); + } + if (munmap(maddr, st.st_size) != 0) + abort(); + if (close(fd) != 0) + abort(); + + return (ret); +} + +static void +varpd_files_stop(void *arg) +{ + varpd_files_t *vaf = arg; + + nvlist_free(vaf->vaf_nvl); + vaf->vaf_nvl = NULL; +} + +static void +varpd_files_destroy(void *arg) +{ + varpd_files_t *vaf = arg; + + assert(vaf->vaf_nvl == NULL); + if (vaf->vaf_path != NULL) { + umem_free(vaf->vaf_path, strlen(vaf->vaf_path) + 1); + vaf->vaf_path = NULL; + } + umem_free(vaf, sizeof (varpd_files_t)); +} + +static void +varpd_files_lookup(void *arg, varpd_query_handle_t *qh, + const overlay_targ_lookup_t *otl, overlay_target_point_t *otp) +{ + char macstr[ETHERADDRSTRL], *ipstr; + nvlist_t *nvl; + varpd_files_t *vaf = arg; + int32_t port; + static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + /* We don't support a default */ + if (otl == NULL) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (otl->otl_sap == ETHERTYPE_ARP) { + libvarpd_plugin_proxy_arp(vaf->vaf_hdl, qh, otl); + return; + } + + if (otl->otl_sap == ETHERTYPE_IPV6 && + otl->otl_dstaddr[0] == 0x33 && + otl->otl_dstaddr[1] == 0x33) { + libvarpd_plugin_proxy_ndp(vaf->vaf_hdl, qh, otl); + return; + } + + if (otl->otl_sap == ETHERTYPE_IP && + bcmp(otl->otl_dstaddr, bcast, ETHERADDRL) == 0) { + char *mac; + struct ether_addr a, *addr; + + addr = &a; + if (ether_ntoa_r((struct ether_addr *)otl->otl_srcaddr, + macstr) == NULL) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (nvlist_lookup_string(nvl, "dhcp-proxy", &mac) != 0) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (ether_aton_r(mac, addr) == NULL) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + libvarpd_plugin_proxy_dhcp(vaf->vaf_hdl, qh, otl); + return; + } + + if (ether_ntoa_r((struct ether_addr *)otl->otl_dstaddr, + macstr) == NULL) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (nvlist_lookup_int32(nvl, "port", &port) != 0) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + if (port <= 0 || port > UINT16_MAX) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + otp->otp_port = port; + + if (nvlist_lookup_string(nvl, "ip", &ipstr) != 0) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + + /* + * Try to parse it as a v6 address and then if it's not, try to + * transform it into a v4 address which we'll then wrap it into a v4 + * mapped address. + */ + if (inet_pton(AF_INET6, ipstr, &otp->otp_ip) != 1) { + uint32_t v4; + if (inet_pton(AF_INET, ipstr, &v4) != 1) { + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_DROP); + return; + } + IN6_IPADDR_TO_V4MAPPED(v4, &otp->otp_ip); + } + + libvarpd_plugin_query_reply(qh, VARPD_LOOKUP_OK); +} + +/* ARGSUSED */ +static int +varpd_files_nprops(void *arg, uint_t *nprops) +{ + *nprops = 1; + return (0); +} + +/* ARGSUSED */ +static int +varpd_files_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph) +{ + if (propid != 0) + return (EINVAL); + + libvarpd_prop_set_name(vph, varpd_files_props[0]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_STRING); + libvarpd_prop_set_nodefault(vph); + return (0); +} + +static int +varpd_files_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep) +{ + varpd_files_t *vaf = arg; + + if (strcmp(pname, varpd_files_props[0]) != 0) + return (EINVAL); + + if (vaf->vaf_path != NULL) { + size_t len = strlen(vaf->vaf_path) + 1; + if (*sizep < len) + return (EOVERFLOW); + *sizep = len; + (void) strlcpy(buf, vaf->vaf_path, *sizep); + + } else { + *sizep = 0; + } + + return (0); +} + +static int +varpd_files_setprop(void *arg, const char *pname, const void *buf, + const uint32_t size) +{ + varpd_files_t *vaf = arg; + + if (strcmp(pname, varpd_files_props[0]) != 0) + return (EINVAL); + + if (vaf->vaf_path != NULL) + umem_free(vaf->vaf_path, strlen(vaf->vaf_path) + 1); + + vaf->vaf_path = umem_alloc(size, UMEM_DEFAULT); + if (vaf->vaf_path == NULL) + return (ENOMEM); + (void) strlcpy(vaf->vaf_path, buf, size); + return (0); +} + +static int +varpd_files_save(void *arg, nvlist_t *nvp) +{ + int ret; + varpd_files_t *vaf = arg; + + if (vaf->vaf_path == NULL) + return (0); + + if ((ret = nvlist_add_string(nvp, varpd_files_props[0], + vaf->vaf_path)) != 0) + return (ret); + + if ((ret = nvlist_add_uint64(nvp, "files/vaf_nmisses", + vaf->vaf_nmisses)) != 0) + return (ret); + + if ((ret = nvlist_add_uint64(nvp, "files/vaf_narp", + vaf->vaf_narp)) != 0) + return (ret); + return (0); +} + +static int +varpd_files_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl, + overlay_plugin_dest_t dest, void **outp) +{ + varpd_files_t *vaf; + char *str; + int ret; + uint64_t nmisses, narp; + + if (varpd_files_valid_dest(dest) == B_FALSE) + return (EINVAL); + + ret = nvlist_lookup_string(nvp, varpd_files_props[0], &str); + if (ret != 0 && ret != ENOENT) + return (ret); + else if (ret == ENOENT) + str = NULL; + + if (nvlist_lookup_uint64(nvp, "files/vaf_nmisses", &nmisses) != 0) + return (EINVAL); + if (nvlist_lookup_uint64(nvp, "files/vaf_narp", &narp) != 0) + return (EINVAL); + + vaf = umem_alloc(sizeof (varpd_files_t), UMEM_DEFAULT); + if (vaf == NULL) + return (ENOMEM); + + bzero(vaf, sizeof (varpd_files_t)); + vaf->vaf_dest = dest; + if (str != NULL) { + size_t len = strlen(str) + 1; + vaf->vaf_path = umem_alloc(len, UMEM_DEFAULT); + if (vaf->vaf_path == NULL) { + umem_free(vaf, sizeof (varpd_files_t)); + return (ENOMEM); + } + (void) strlcpy(vaf->vaf_path, str, len); + } + + vaf->vaf_hdl = hdl; + *outp = vaf; + return (0); +} + +static void +varpd_files_proxy_arp(void *arg, varpd_arp_handle_t *vah, int kind, + const struct sockaddr *sock, uint8_t *out) +{ + varpd_files_t *vaf = arg; + const struct sockaddr_in *ip; + const struct sockaddr_in6 *ip6; + nvpair_t *pair; + + if (kind != VARPD_QTYPE_ETHERNET) { + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); + return; + } + + if (sock->sa_family != AF_INET && sock->sa_family != AF_INET6) { + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); + return; + } + + ip = (const struct sockaddr_in *)sock; + ip6 = (const struct sockaddr_in6 *)sock; + for (pair = nvlist_next_nvpair(vaf->vaf_nvl, NULL); pair != NULL; + pair = nvlist_next_nvpair(vaf->vaf_nvl, pair)) { + char *mac, *ipstr; + nvlist_t *data; + struct in_addr ia; + struct in6_addr ia6; + struct ether_addr ether, *e; + e = ðer; + + if (nvpair_type(pair) != DATA_TYPE_NVLIST) + continue; + + mac = nvpair_name(pair); + if (nvpair_value_nvlist(pair, &data) != 0) + continue; + + + if (sock->sa_family == AF_INET) { + if (nvlist_lookup_string(data, "arp", &ipstr) != 0) + continue; + + if (inet_pton(AF_INET, ipstr, &ia) != 1) + continue; + + if (bcmp(&ia, &ip->sin_addr, + sizeof (struct in_addr)) != 0) + continue; + } else { + if (nvlist_lookup_string(data, "ndp", &ipstr) != 0) + continue; + + if (inet_pton(AF_INET6, ipstr, &ia6) != 1) + continue; + + if (bcmp(&ia6, &ip6->sin6_addr, + sizeof (struct in6_addr)) != 0) + continue; + } + + if (ether_aton_r(mac, e) == NULL) { + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); + return; + } + + bcopy(e, out, ETHERADDRL); + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_OK); + return; + } + + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); +} + +static void +varpd_files_proxy_dhcp(void *arg, varpd_dhcp_handle_t *vdh, int type, + const overlay_targ_lookup_t *otl, uint8_t *out) +{ + varpd_files_t *vaf = arg; + nvlist_t *nvl; + char macstr[ETHERADDRSTRL], *mac; + struct ether_addr a, *addr; + + addr = &a; + if (type != VARPD_QTYPE_ETHERNET) { + libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); + return; + } + + if (ether_ntoa_r((struct ether_addr *)otl->otl_srcaddr, + macstr) == NULL) { + libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); + return; + } + + if (nvlist_lookup_nvlist(vaf->vaf_nvl, macstr, &nvl) != 0) { + libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); + return; + } + + if (nvlist_lookup_string(nvl, "dhcp-proxy", &mac) != 0) { + libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); + return; + } + + if (ether_aton_r(mac, addr) == NULL) { + libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_DROP); + return; + } + + bcopy(addr, out, ETHERADDRL); + libvarpd_plugin_dhcp_reply(vdh, VARPD_LOOKUP_OK); +} + +static const varpd_plugin_ops_t varpd_files_ops = { + 0, + varpd_files_create, + varpd_files_start, + varpd_files_stop, + varpd_files_destroy, + NULL, + varpd_files_lookup, + varpd_files_nprops, + varpd_files_propinfo, + varpd_files_getprop, + varpd_files_setprop, + varpd_files_save, + varpd_files_restore, + varpd_files_proxy_arp, + varpd_files_proxy_dhcp +}; + +#pragma init(varpd_files_init) +static void +varpd_files_init(void) +{ + int err; + varpd_plugin_register_t *vpr; + + vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err); + if (vpr == NULL) + return; + + vpr->vpr_mode = OVERLAY_TARGET_DYNAMIC; + vpr->vpr_name = "files"; + vpr->vpr_ops = &varpd_files_ops; + (void) libvarpd_plugin_register(vpr); + libvarpd_plugin_free(vpr); +} diff --git a/usr/src/lib/varpd/files/common/libvarpd_files_json.c b/usr/src/lib/varpd/files/common/libvarpd_files_json.c new file mode 100644 index 0000000000..53e63c6244 --- /dev/null +++ b/usr/src/lib/varpd/files/common/libvarpd_files_json.c @@ -0,0 +1,936 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <strings.h> +#include <errno.h> +#include <libnvpair.h> +#include <sys/ccompile.h> + +#include "libvarpd_files_json.h" + +typedef enum json_type { + JSON_TYPE_NOTHING = 0, + JSON_TYPE_STRING = 1, + JSON_TYPE_INTEGER, + JSON_TYPE_DOUBLE, + JSON_TYPE_BOOLEAN, + JSON_TYPE_NULL, + JSON_TYPE_OBJECT, + JSON_TYPE_ARRAY +} json_type_t; + +typedef enum parse_state { + PARSE_ERROR = -1, + PARSE_DONE = 0, + PARSE_REST, + PARSE_OBJECT, + PARSE_KEY_STRING, + PARSE_COLON, + PARSE_STRING, + PARSE_OBJECT_COMMA, + PARSE_ARRAY, + PARSE_BAREWORD, + PARSE_NUMBER, + PARSE_ARRAY_VALUE, + PARSE_ARRAY_COMMA +} parse_state_t; + +#define JSON_MARKER ".__json_" +#define JSON_MARKER_ARRAY JSON_MARKER "array" + +typedef struct parse_frame { + parse_state_t pf_ps; + nvlist_t *pf_nvl; + + char *pf_key; + void *pf_value; + json_type_t pf_value_type; + int pf_array_index; + + struct parse_frame *pf_next; +} parse_frame_t; + +typedef struct state { + const char *s_in; + unsigned long s_pos; + unsigned long s_len; + + parse_frame_t *s_top; + + nvlist_parse_json_flags_t s_flags; + + /* + * This string buffer is used for temporary storage by the + * "collect_*()" family of functions. + */ + custr_t *s_collect; + + int s_errno; + custr_t *s_errstr; +} state_t; + +typedef void (*parse_handler_t)(state_t *); + +static void +movestate(state_t *s, parse_state_t ps) +{ + if (s->s_flags & NVJSON_DEBUG) { + (void) fprintf(stderr, "nvjson: move state %d -> %d\n", + s->s_top->pf_ps, ps); + } + s->s_top->pf_ps = ps; +} + +static void +posterror(state_t *s, int erno, const char *error) +{ + /* + * If the caller wants error messages printed to stderr, do that + * first. + */ + if (s->s_flags & NVJSON_ERRORS_TO_STDERR) { + (void) fprintf(stderr, "nvjson error (pos %ld, errno %d): %s\n", + s->s_pos, erno, error); + } + + /* + * Try and store the error message for the caller. This may fail if + * the error was related to memory pressure, and that condition still + * exists. + */ + s->s_errno = erno; + if (s->s_errstr != NULL) { + (void) custr_append(s->s_errstr, error); + } + + movestate(s, PARSE_ERROR); +} + +static int +pushstate(state_t *s, parse_state_t ps, parse_state_t retps) +{ + parse_frame_t *n; + + if (s->s_flags & NVJSON_DEBUG) { + (void) fprintf(stderr, "nvjson: push state %d -> %d (ret %d)\n", + s->s_top->pf_ps, ps, retps); + } + + if ((n = calloc(1, sizeof (*n))) == NULL) { + posterror(s, errno, "pushstate calloc failure"); + return (-1); + } + + /* + * Store the state we'll return to when popping this + * frame: + */ + s->s_top->pf_ps = retps; + + /* + * Store the initial state for the new frame, and + * put it on top of the stack: + */ + n->pf_ps = ps; + n->pf_value_type = JSON_TYPE_NOTHING; + + n->pf_next = s->s_top; + s->s_top = n; + + return (0); +} + +static char +popchar(state_t *s) +{ + if (s->s_pos > s->s_len) { + return (0); + } + return (s->s_in[s->s_pos++]); +} + +static char +peekchar(state_t *s) +{ + if (s->s_pos > s->s_len) { + return (0); + } + return (s->s_in[s->s_pos]); +} + +static void +discard_whitespace(state_t *s) +{ + while (isspace(peekchar(s))) { + (void) popchar(s); + } +} + +static char *escape_pairs[] = { + "\"\"", "\\\\", "//", "b\b", "f\f", "n\n", "r\r", "t\t", NULL +}; + +static char +collect_string_escape(state_t *s) +{ + int i; + char c = popchar(s); + + if (c == '\0') { + posterror(s, EPROTO, "EOF mid-escape sequence"); + return (-1); + } + + /* + * Handle four-digit Unicode escapes up to and including \u007f. + * Strings that cannot be represented as 7-bit clean ASCII are not + * currently supported. + */ + if (c == 'u') { + int res; + int ndigs = 0; + char digs[5]; + + /* + * Deal with 4-digit unicode escape. + */ + while (ndigs < 4) { + if ((digs[ndigs++] = popchar(s)) == '\0') { + posterror(s, EPROTO, "EOF mid-escape " + "sequence"); + return (-1); + } + } + digs[4] = '\0'; + if ((res = atoi(digs)) > 127) { + posterror(s, EPROTO, "unicode escape above 0x7f"); + return (-1); + } + + if (custr_appendc(s->s_collect, res) != 0) { + posterror(s, errno, "custr_appendc failure"); + return (-1); + } + return (0); + } + + /* + * See if this is a C-style escape character we recognise. + */ + for (i = 0; escape_pairs[i] != NULL; i++) { + char *ep = escape_pairs[i]; + if (ep[0] == c) { + if (custr_appendc(s->s_collect, ep[1]) != 0) { + posterror(s, errno, "custr_appendc failure"); + return (-1); + } + return (0); + } + } + + posterror(s, EPROTO, "unrecognised escape sequence"); + return (-1); +} + +static int +collect_string(state_t *s) +{ + custr_reset(s->s_collect); + + for (;;) { + char c; + + switch (c = popchar(s)) { + case '"': + /* + * Legal End of String. + */ + return (0); + + case '\0': + posterror(s, EPROTO, "EOF mid-string"); + return (-1); + + case '\\': + /* + * Escape Characters and Sequences. + */ + if (collect_string_escape(s) != 0) { + return (-1); + } + break; + + default: + if (custr_appendc(s->s_collect, c) != 0) { + posterror(s, errno, "custr_appendc failure"); + return (-1); + } + break; + } + } +} + +static int +collect_bareword(state_t *s) +{ + custr_reset(s->s_collect); + + for (;;) { + if (!islower(peekchar(s))) { + return (0); + } + + if (custr_appendc(s->s_collect, popchar(s)) != 0) { + posterror(s, errno, "custr_appendc failure"); + return (-1); + } + } +} + +static void +hdlr_bareword(state_t *s) +{ + const char *str; + + if (collect_bareword(s) != 0) { + return; + } + + str = custr_cstr(s->s_collect); + if (strcmp(str, "true") == 0) { + s->s_top->pf_value_type = JSON_TYPE_BOOLEAN; + s->s_top->pf_value = (void *)B_TRUE; + } else if (strcmp(str, "false") == 0) { + s->s_top->pf_value_type = JSON_TYPE_BOOLEAN; + s->s_top->pf_value = (void *)B_FALSE; + } else if (strcmp(str, "null") == 0) { + s->s_top->pf_value_type = JSON_TYPE_NULL; + } else { + posterror(s, EPROTO, "expected 'true', 'false' or 'null'"); + return; + } + + movestate(s, PARSE_DONE); +} + +/* ARGSUSED */ +static int +collect_number(state_t *s, boolean_t *isint, int32_t *result, + double *fresult __unused) +{ + boolean_t neg = B_FALSE; + int t; + + custr_reset(s->s_collect); + + if (peekchar(s) == '-') { + neg = B_TRUE; + (void) popchar(s); + } + /* + * Read the 'int' portion: + */ + if (!isdigit(peekchar(s))) { + posterror(s, EPROTO, "malformed number: expected digit (0-9)"); + return (-1); + } + for (;;) { + if (!isdigit(peekchar(s))) { + break; + } + if (custr_appendc(s->s_collect, popchar(s)) != 0) { + posterror(s, errno, "custr_append failure"); + return (-1); + } + } + if (peekchar(s) == '.' || peekchar(s) == 'e' || peekchar(s) == 'E') { + posterror(s, ENOTSUP, "do not yet support FRACs or EXPs"); + return (-1); + } + + t = atoi(custr_cstr(s->s_collect)); + + *isint = B_TRUE; + *result = (neg == B_TRUE) ? (-t) : t; + return (0); +} + +static void +hdlr_number(state_t *s) +{ + boolean_t isint; + int32_t result; + double fresult; + + if (collect_number(s, &isint, &result, &fresult) != 0) { + return; + } + + if (isint == B_TRUE) { + s->s_top->pf_value = (void *)(uintptr_t)result; + s->s_top->pf_value_type = JSON_TYPE_INTEGER; + } else { + s->s_top->pf_value = malloc(sizeof (fresult)); + bcopy(&fresult, s->s_top->pf_value, sizeof (fresult)); + s->s_top->pf_value_type = JSON_TYPE_DOUBLE; + } + + movestate(s, PARSE_DONE); +} + +static void +hdlr_rest(state_t *s) +{ + char c; + discard_whitespace(s); + c = popchar(s); + switch (c) { + case '{': + movestate(s, PARSE_OBJECT); + return; + + case '[': + movestate(s, PARSE_ARRAY); + return; + + default: + posterror(s, EPROTO, "EOF before object or array"); + return; + } +} + +static int +add_empty_child(state_t *s) +{ + /* + * Here, we create an empty nvlist to represent this object + * or array: + */ + nvlist_t *empty; + if (nvlist_alloc(&empty, NV_UNIQUE_NAME, 0) != 0) { + posterror(s, errno, "nvlist_alloc failure"); + return (-1); + } + if (s->s_top->pf_next != NULL) { + /* + * If we're a child of the frame above, we store ourselves in + * that frame's nvlist: + */ + nvlist_t *nvl = s->s_top->pf_next->pf_nvl; + char *key = s->s_top->pf_next->pf_key; + + if (nvlist_add_nvlist(nvl, key, empty) != 0) { + posterror(s, errno, "nvlist_add_nvlist failure"); + nvlist_free(empty); + return (-1); + } + nvlist_free(empty); + if (nvlist_lookup_nvlist(nvl, key, &empty) != 0) { + posterror(s, errno, "nvlist_lookup_nvlist failure"); + return (-1); + } + } + s->s_top->pf_nvl = empty; + return (0); +} + +static int +decorate_array(state_t *s) +{ + int idx = s->s_top->pf_array_index; + /* + * When we are done creating an array, we store a 'length' + * property on it, as well as an internal-use marker value. + */ + if (nvlist_add_boolean(s->s_top->pf_nvl, JSON_MARKER_ARRAY) != 0 || + nvlist_add_uint32(s->s_top->pf_nvl, "length", idx) != 0) { + posterror(s, errno, "nvlist_add failure"); + return (-1); + } + + return (0); +} + +static void +hdlr_array(state_t *s) +{ + s->s_top->pf_value_type = JSON_TYPE_ARRAY; + + if (add_empty_child(s) != 0) { + return; + } + + discard_whitespace(s); + + switch (peekchar(s)) { + case ']': + (void) popchar(s); + + if (decorate_array(s) != 0) { + return; + } + + movestate(s, PARSE_DONE); + return; + + default: + movestate(s, PARSE_ARRAY_VALUE); + return; + } +} + +static void +hdlr_array_comma(state_t *s) +{ + discard_whitespace(s); + + switch (popchar(s)) { + case ']': + if (decorate_array(s) != 0) { + return; + } + + movestate(s, PARSE_DONE); + return; + case ',': + movestate(s, PARSE_ARRAY_VALUE); + return; + default: + posterror(s, EPROTO, "expected ',' or ']'"); + return; + } +} + +static void +hdlr_array_value(state_t *s) +{ + char c; + + /* + * Generate keyname from the next array index: + */ + if (s->s_top->pf_key != NULL) { + (void) fprintf(stderr, "pf_key not null! was %s\n", + s->s_top->pf_key); + abort(); + } + + if (asprintf(&s->s_top->pf_key, "%d", s->s_top->pf_array_index++) < 0) { + posterror(s, errno, "asprintf failure"); + return; + } + + discard_whitespace(s); + + /* + * Select which type handler we need for the next value: + */ + switch (c = peekchar(s)) { + case '"': + (void) popchar(s); + (void) pushstate(s, PARSE_STRING, PARSE_ARRAY_COMMA); + return; + + case '{': + (void) popchar(s); + (void) pushstate(s, PARSE_OBJECT, PARSE_ARRAY_COMMA); + return; + + case '[': + (void) popchar(s); + (void) pushstate(s, PARSE_ARRAY, PARSE_ARRAY_COMMA); + return; + + default: + if (islower(c)) { + (void) pushstate(s, PARSE_BAREWORD, + PARSE_ARRAY_COMMA); + return; + } else if (c == '-' || isdigit(c)) { + (void) pushstate(s, PARSE_NUMBER, PARSE_ARRAY_COMMA); + return; + } else { + posterror(s, EPROTO, "unexpected character at start " + "of value"); + return; + } + } +} + +static void +hdlr_object(state_t *s) +{ + s->s_top->pf_value_type = JSON_TYPE_OBJECT; + + if (add_empty_child(s) != 0) { + return; + } + + discard_whitespace(s); + + switch (popchar(s)) { + case '}': + movestate(s, PARSE_DONE); + return; + + case '"': + movestate(s, PARSE_KEY_STRING); + return; + + default: + posterror(s, EPROTO, "expected key or '}'"); + return; + } +} + +static void +hdlr_key_string(state_t *s) +{ + if (collect_string(s) != 0) { + return; + } + + /* + * Record the key name of the next value. + */ + if ((s->s_top->pf_key = strdup(custr_cstr(s->s_collect))) == NULL) { + posterror(s, errno, "strdup failure"); + return; + } + + movestate(s, PARSE_COLON); +} + +static void +hdlr_colon(state_t *s) +{ + char c; + discard_whitespace(s); + + if ((c = popchar(s)) != ':') { + posterror(s, EPROTO, "expected ':'"); + return; + } + + discard_whitespace(s); + + /* + * Select which type handler we need for the value after the colon: + */ + switch (c = peekchar(s)) { + case '"': + (void) popchar(s); + (void) pushstate(s, PARSE_STRING, PARSE_OBJECT_COMMA); + return; + + case '{': + (void) popchar(s); + (void) pushstate(s, PARSE_OBJECT, PARSE_OBJECT_COMMA); + return; + + case '[': + (void) popchar(s); + (void) pushstate(s, PARSE_ARRAY, PARSE_OBJECT_COMMA); + return; + + default: + if (islower(c)) { + (void) pushstate(s, PARSE_BAREWORD, PARSE_OBJECT_COMMA); + return; + } else if (c == '-' || isdigit(c)) { + (void) pushstate(s, PARSE_NUMBER, PARSE_OBJECT_COMMA); + return; + } else { + (void) posterror(s, EPROTO, "unexpected character at " + "start of value"); + return; + } + } +} + +static void +hdlr_object_comma(state_t *s) +{ + discard_whitespace(s); + + switch (popchar(s)) { + case '}': + movestate(s, PARSE_DONE); + return; + + case ',': + discard_whitespace(s); + if (popchar(s) != '"') { + posterror(s, EPROTO, "expected '\"'"); + return; + } + movestate(s, PARSE_KEY_STRING); + return; + + default: + posterror(s, EPROTO, "expected ',' or '}'"); + return; + } +} + +static void +hdlr_string(state_t *s) +{ + if (collect_string(s) != 0) { + return; + } + + s->s_top->pf_value_type = JSON_TYPE_STRING; + if ((s->s_top->pf_value = strdup(custr_cstr(s->s_collect))) == NULL) { + posterror(s, errno, "strdup failure"); + return; + } + + movestate(s, PARSE_DONE); +} + +static int +store_value(state_t *s) +{ + nvlist_t *targ = s->s_top->pf_next->pf_nvl; + char *key = s->s_top->pf_next->pf_key; + json_type_t type = s->s_top->pf_value_type; + int ret = 0; + + switch (type) { + case JSON_TYPE_STRING: + if (nvlist_add_string(targ, key, s->s_top->pf_value) != 0) { + posterror(s, errno, "nvlist_add_string failure"); + ret = -1; + } + free(s->s_top->pf_value); + break; + + case JSON_TYPE_BOOLEAN: + if (nvlist_add_boolean_value(targ, key, + (boolean_t)s->s_top->pf_value) != 0) { + posterror(s, errno, "nvlist_add_boolean_value " + "failure"); + ret = -1; + } + break; + + case JSON_TYPE_NULL: + if (nvlist_add_boolean(targ, key) != 0) { + posterror(s, errno, "nvlist_add_boolean failure"); + ret = -1; + } + break; + + case JSON_TYPE_INTEGER: + if (nvlist_add_int32(targ, key, + (int32_t)(uintptr_t)s->s_top->pf_value) != 0) { + posterror(s, errno, "nvlist_add_int32 failure"); + ret = -1; + } + break; + + case JSON_TYPE_ARRAY: + case JSON_TYPE_OBJECT: + /* + * Objects and arrays are already 'stored' in their target + * nvlist on creation. See: hdlr_object, hdlr_array. + */ + break; + + default: + (void) fprintf(stderr, "ERROR: could not store unknown " + "type %d\n", type); + abort(); + } + + s->s_top->pf_value = NULL; + free(s->s_top->pf_next->pf_key); + s->s_top->pf_next->pf_key = NULL; + return (ret); +} + +static parse_frame_t * +parse_frame_free(parse_frame_t *pf, boolean_t free_nvl) +{ + parse_frame_t *next = pf->pf_next; + if (pf->pf_key != NULL) { + free(pf->pf_key); + } + if (pf->pf_value != NULL) { + abort(); + } + if (free_nvl && pf->pf_nvl != NULL) { + nvlist_free(pf->pf_nvl); + } + free(pf); + return (next); +} + +static parse_handler_t hdlrs[] = { + NULL, /* PARSE_DONE */ + hdlr_rest, /* PARSE_REST */ + hdlr_object, /* PARSE_OBJECT */ + hdlr_key_string, /* PARSE_KEY_STRING */ + hdlr_colon, /* PARSE_COLON */ + hdlr_string, /* PARSE_STRING */ + hdlr_object_comma, /* PARSE_OBJECT_COMMA */ + hdlr_array, /* PARSE_ARRAY */ + hdlr_bareword, /* PARSE_BAREWORD */ + hdlr_number, /* PARSE_NUMBER */ + hdlr_array_value, /* PARSE_ARRAY_VALUE */ + hdlr_array_comma /* PARSE_ARRAY_COMMA */ +}; +#define NUM_PARSE_HANDLERS (int)(sizeof (hdlrs) / sizeof (hdlrs[0])) + +int +nvlist_parse_json(const char *buf, size_t buflen, nvlist_t **nvlp, + nvlist_parse_json_flags_t flag, nvlist_parse_json_error_t *errout) +{ + state_t s; + + /* + * Check for valid flags: + */ + if ((flag & NVJSON_FORCE_INTEGER) && (flag & NVJSON_FORCE_DOUBLE)) { + errno = EINVAL; + return (-1); + } + if ((flag & ~NVJSON_ALL) != 0) { + errno = EINVAL; + return (-1); + } + + /* + * Initialise parsing state structure: + */ + bzero(&s, sizeof (s)); + s.s_in = buf; + s.s_pos = 0; + s.s_len = buflen; + s.s_flags = flag; + + /* + * Allocate the collect buffer string. + */ + if (custr_alloc(&s.s_collect) != 0) { + s.s_errno = errno; + if (errout != NULL) { + (void) snprintf(errout->nje_message, + sizeof (errout->nje_message), + "custr alloc failure: %s", + strerror(errno)); + } + goto out; + } + + /* + * If the caller has requested error information, allocate the error + * string now. + */ + if (errout != NULL) { + if (custr_alloc_buf(&s.s_errstr, errout->nje_message, + sizeof (errout->nje_message)) != 0) { + s.s_errno = errno; + (void) snprintf(errout->nje_message, + sizeof (errout->nje_message), + "custr alloc failure: %s", + strerror(errno)); + goto out; + } + custr_reset(s.s_errstr); + } + + /* + * Allocate top-most stack frame: + */ + if ((s.s_top = calloc(1, sizeof (*s.s_top))) == NULL) { + s.s_errno = errno; + goto out; + } + + s.s_top->pf_ps = PARSE_REST; + for (;;) { + if (s.s_top->pf_ps < 0) { + /* + * The parser reported an error. + */ + goto out; + } + + if (s.s_top->pf_ps == PARSE_DONE) { + if (s.s_top->pf_next == NULL) { + /* + * Last frame, so we're really + * done. + */ + *nvlp = s.s_top->pf_nvl; + goto out; + } else { + /* + * Otherwise, pop a frame and continue in + * previous state. Copy out the value we + * created in the old frame: + */ + if (store_value(&s) != 0) { + goto out; + } + + /* + * Free old frame: + */ + s.s_top = parse_frame_free(s.s_top, B_FALSE); + } + } + + /* + * Dispatch to parser handler routine for this state: + */ + if (s.s_top->pf_ps >= NUM_PARSE_HANDLERS || + hdlrs[s.s_top->pf_ps] == NULL) { + (void) fprintf(stderr, "no handler for state %d\n", + s.s_top->pf_ps); + abort(); + } + hdlrs[s.s_top->pf_ps](&s); + } + +out: + if (errout != NULL) { + /* + * Copy out error number and parse position. The custr_t for + * the error message was backed by the buffer in the error + * object, so no copying is required. + */ + errout->nje_errno = s.s_errno; + errout->nje_pos = s.s_pos; + } + + /* + * Free resources: + */ + while (s.s_top != NULL) { + s.s_top = parse_frame_free(s.s_top, s.s_errno == 0 ? B_FALSE : + B_TRUE); + } + custr_free(s.s_collect); + custr_free(s.s_errstr); + + errno = s.s_errno; + return (s.s_errno == 0 ? 0 : -1); +} diff --git a/usr/src/lib/varpd/files/common/libvarpd_files_json.h b/usr/src/lib/varpd/files/common/libvarpd_files_json.h new file mode 100644 index 0000000000..9fe765741b --- /dev/null +++ b/usr/src/lib/varpd/files/common/libvarpd_files_json.h @@ -0,0 +1,52 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Joyent, Inc. + */ + +#ifndef _LIBVARPD_FILES_JSON_H +#define _LIBVARPD_FILES_JSON_H + +#include <libnvpair.h> +#include <libcustr.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum nvlist_parse_json_flags { + NVJSON_FORCE_INTEGER = 0x01, + NVJSON_FORCE_DOUBLE = 0x02, + NVJSON_ERRORS_TO_STDERR = 0x04, + NVJSON_DEBUG = 0x08 +} nvlist_parse_json_flags_t; + +typedef struct nvlist_parse_json_error { + int nje_errno; + long nje_pos; + char nje_message[512]; +} nvlist_parse_json_error_t; + +#define NVJSON_ALL \ + (NVJSON_FORCE_INTEGER | \ + NVJSON_FORCE_DOUBLE | \ + NVJSON_ERRORS_TO_STDERR | \ + NVJSON_DEBUG) + +extern int nvlist_parse_json(const char *, size_t, nvlist_t **, + nvlist_parse_json_flags_t, nvlist_parse_json_error_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_FILES_JSON_H */ diff --git a/usr/src/lib/varpd/files/common/llib-lvarpd_files b/usr/src/lib/varpd/files/common/llib-lvarpd_files new file mode 100644 index 0000000000..03c34f4fcb --- /dev/null +++ b/usr/src/lib/varpd/files/common/llib-lvarpd_files @@ -0,0 +1,18 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + diff --git a/usr/src/lib/varpd/files/common/mapfile-vers b/usr/src/lib/varpd/files/common/mapfile-vers new file mode 100644 index 0000000000..6b7c5a5067 --- /dev/null +++ b/usr/src/lib/varpd/files/common/mapfile-vers @@ -0,0 +1,35 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate { + local: + *; +}; diff --git a/usr/src/lib/varpd/files/i386/Makefile b/usr/src/lib/varpd/files/i386/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/files/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/files/sparc/Makefile b/usr/src/lib/varpd/files/sparc/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/files/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/files/sparcv9/Makefile b/usr/src/lib/varpd/files/sparcv9/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/files/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/libvarpd/Makefile b/usr/src/lib/varpd/libvarpd/Makefile new file mode 100644 index 0000000000..2a4f8f070c --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/Makefile @@ -0,0 +1,55 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../../Makefile.lib + +HDRS = libvarpd.h libvarpd_client.h libvarpd_provider.h +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +TYPECHECK_LIB = libvarpd.so.1 +TYPELIST = \ + varpd_client_instance_arg_t \ + varpd_client_nprops_arg_t \ + varpd_client_propinfo_arg_t \ + varpd_client_eresp_t \ + varpd_persist_header_t \ + overlay_targ_cache_entry_t \ + overlay_targ_cache_t \ + overlay_targ_cache_iter_t + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber lint: $(SUBDIRS) + +install: $(SUBDIRS) $(VARPD_MAPFILES) install_h + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) $(TYPECHECK) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ diff --git a/usr/src/lib/varpd/libvarpd/Makefile.com b/usr/src/lib/varpd/libvarpd/Makefile.com new file mode 100644 index 0000000000..1521f83f8f --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/Makefile.com @@ -0,0 +1,53 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +LIBRARY = libvarpd.a +VERS = .1 +OBJECTS = libvarpd.o \ + libvarpd_arp.o \ + libvarpd_client.o \ + libvarpd_door.o \ + libvarpd_overlay.o \ + libvarpd_panic.o \ + libvarpd_persist.o \ + libvarpd_prop.o \ + libvarpd_plugin.o \ + libvarpd_util.o + +include ../../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lavl -lumem -lidspace -lnvpair -lmd5 -lrename \ + -lbunyan +CPPFLAGS += -I../common + +CERRWARN += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR +LINTFLAGS += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR \ + -erroff=E_BAD_PTR_CAST_ALIGN +LINTFLAGS64 += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR \ + -erroff=E_BAD_PTR_CAST_ALIGN + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +SRCDIR = ../common + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../Makefile.targ diff --git a/usr/src/lib/varpd/libvarpd/amd64/Makefile b/usr/src/lib/varpd/libvarpd/amd64/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd.c b/usr/src/lib/varpd/libvarpd/common/libvarpd.c new file mode 100644 index 0000000000..e4460089cc --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd.c @@ -0,0 +1,360 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * varpd library + */ + +#include <stdlib.h> +#include <errno.h> +#include <umem.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/avl.h> +#include <stddef.h> +#include <stdio.h> +#include <strings.h> + +#include <libvarpd_impl.h> + +static int +libvarpd_instance_comparator(const void *lp, const void *rp) +{ + const varpd_instance_t *lpp, *rpp; + lpp = lp; + rpp = rp; + + if (lpp->vri_id > rpp->vri_id) + return (1); + if (lpp->vri_id < rpp->vri_id) + return (-1); + return (0); +} + +static int +libvarpd_instance_lcomparator(const void *lp, const void *rp) +{ + const varpd_instance_t *lpp, *rpp; + lpp = lp; + rpp = rp; + + if (lpp->vri_linkid > rpp->vri_linkid) + return (1); + if (lpp->vri_linkid < rpp->vri_linkid) + return (-1); + return (0); +} + +int +libvarpd_create(varpd_handle_t **vphp) +{ + int ret; + varpd_impl_t *vip; + char buf[32]; + + if (vphp == NULL) + return (EINVAL); + + *vphp = NULL; + vip = umem_alloc(sizeof (varpd_impl_t), UMEM_DEFAULT); + if (vip == NULL) + return (errno); + + bzero(vip, sizeof (varpd_impl_t)); + (void) snprintf(buf, sizeof (buf), "varpd_%p", vip); + vip->vdi_idspace = id_space_create(buf, LIBVARPD_ID_MIN, + LIBVARPD_ID_MAX); + if (vip->vdi_idspace == NULL) { + int ret = errno; + umem_free(vip, sizeof (varpd_impl_t)); + return (ret); + } + + vip->vdi_qcache = umem_cache_create("query", sizeof (varpd_query_t), 0, + NULL, NULL, NULL, NULL, NULL, 0); + if (vip->vdi_qcache == NULL) { + int ret = errno; + id_space_destroy(vip->vdi_idspace); + umem_free(vip, sizeof (varpd_impl_t)); + return (ret); + } + + if ((ret = libvarpd_overlay_init(vip)) != 0) { + umem_cache_destroy(vip->vdi_qcache); + id_space_destroy(vip->vdi_idspace); + umem_free(vip, sizeof (varpd_impl_t)); + return (ret); + } + + if ((ret = bunyan_init("varpd", &vip->vdi_bunyan)) != 0) { + libvarpd_overlay_fini(vip); + umem_cache_destroy(vip->vdi_qcache); + id_space_destroy(vip->vdi_idspace); + umem_free(vip, sizeof (varpd_impl_t)); + return (ret); + } + + libvarpd_persist_init(vip); + + avl_create(&vip->vdi_plugins, libvarpd_plugin_comparator, + sizeof (varpd_plugin_t), offsetof(varpd_plugin_t, vpp_node)); + + avl_create(&vip->vdi_instances, libvarpd_instance_comparator, + sizeof (varpd_instance_t), offsetof(varpd_instance_t, vri_inode)); + avl_create(&vip->vdi_linstances, libvarpd_instance_lcomparator, + sizeof (varpd_instance_t), offsetof(varpd_instance_t, vri_lnode)); + + if (mutex_init(&vip->vdi_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL) != 0) + libvarpd_panic("failed to create mutex: %d", errno); + + vip->vdi_doorfd = -1; + *vphp = (varpd_handle_t *)vip; + return (0); +} + +void +libvarpd_destroy(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + libvarpd_overlay_lookup_quiesce(vhp); + if (mutex_destroy(&vip->vdi_lock) != 0) + libvarpd_panic("failed to destroy mutex: %d", errno); + libvarpd_persist_fini(vip); + libvarpd_overlay_fini(vip); + umem_cache_destroy(vip->vdi_qcache); + id_space_destroy(vip->vdi_idspace); + umem_free(vip, sizeof (varpd_impl_t)); +} + +int +libvarpd_instance_create(varpd_handle_t *vhp, datalink_id_t linkid, + const char *pname, varpd_instance_handle_t **outp) +{ + int ret; + varpd_impl_t *vip = (varpd_impl_t *)vhp; + varpd_plugin_t *plugin; + varpd_instance_t *inst, lookup; + overlay_plugin_dest_t dest; + uint64_t vid; + + /* + * We should really have our own errnos. + */ + plugin = libvarpd_plugin_lookup(vip, pname); + if (plugin == NULL) + return (ENOENT); + + if ((ret = libvarpd_overlay_info(vip, linkid, &dest, NULL, &vid)) != 0) + return (ret); + + inst = umem_alloc(sizeof (varpd_instance_t), UMEM_DEFAULT); + if (inst == NULL) + return (ENOMEM); + + inst->vri_id = id_alloc(vip->vdi_idspace); + if (inst->vri_id == -1) + libvarpd_panic("failed to allocate id from vdi_idspace: %d", + errno); + inst->vri_linkid = linkid; + inst->vri_vnetid = vid; + inst->vri_mode = plugin->vpp_mode; + inst->vri_dest = dest; + inst->vri_plugin = plugin; + inst->vri_impl = vip; + inst->vri_flags = 0; + if ((ret = plugin->vpp_ops->vpo_create((varpd_provider_handle_t *)inst, + &inst->vri_private, dest)) != 0) { + id_free(vip->vdi_idspace, inst->vri_id); + umem_free(inst, sizeof (varpd_instance_t)); + return (ret); + } + + if (mutex_init(&inst->vri_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL) != 0) + libvarpd_panic("failed to create mutex: %d", errno); + + mutex_enter(&vip->vdi_lock); + lookup.vri_id = inst->vri_id; + if (avl_find(&vip->vdi_instances, &lookup, NULL) != NULL) + libvarpd_panic("found duplicate instance with id %d", + lookup.vri_id); + avl_add(&vip->vdi_instances, inst); + lookup.vri_linkid = inst->vri_linkid; + if (avl_find(&vip->vdi_linstances, &lookup, NULL) != NULL) + libvarpd_panic("found duplicate linstance with id %d", + lookup.vri_linkid); + avl_add(&vip->vdi_linstances, inst); + mutex_exit(&vip->vdi_lock); + *outp = (varpd_instance_handle_t *)inst; + return (0); +} + +uint64_t +libvarpd_instance_id(varpd_instance_handle_t *ihp) +{ + varpd_instance_t *inst = (varpd_instance_t *)ihp; + return (inst->vri_id); +} + +uint64_t +libvarpd_plugin_vnetid(varpd_provider_handle_t *vhp) +{ + varpd_instance_t *inst = (varpd_instance_t *)vhp; + return (inst->vri_vnetid); +} + +varpd_instance_handle_t * +libvarpd_instance_lookup(varpd_handle_t *vhp, uint64_t id) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + varpd_instance_t lookup, *retp; + + lookup.vri_id = id; + mutex_enter(&vip->vdi_lock); + retp = avl_find(&vip->vdi_instances, &lookup, NULL); + mutex_exit(&vip->vdi_lock); + return ((varpd_instance_handle_t *)retp); +} + +/* + * If this function becomes external to varpd, we need to change it to return a + * varpd_instance_handle_t. + */ +varpd_instance_t * +libvarpd_instance_lookup_by_dlid(varpd_impl_t *vip, datalink_id_t linkid) +{ + varpd_instance_t lookup, *retp; + + lookup.vri_linkid = linkid; + mutex_enter(&vip->vdi_lock); + retp = avl_find(&vip->vdi_linstances, &lookup, NULL); + mutex_exit(&vip->vdi_lock); + return (retp); +} + +/* + * When an instance is being destroyed, that means we should deactivate it, as + * well as clean it up. That means here, the proper order is calling the plug-in + * stop and then the destroy function. + */ +void +libvarpd_instance_destroy(varpd_instance_handle_t *ihp) +{ + varpd_instance_t *inst = (varpd_instance_t *)ihp; + varpd_impl_t *vip = inst->vri_impl; + + /* + * First things first, remove it from global visibility. + */ + mutex_enter(&vip->vdi_lock); + avl_remove(&vip->vdi_instances, inst); + avl_remove(&vip->vdi_linstances, inst); + mutex_exit(&vip->vdi_lock); + + mutex_enter(&inst->vri_lock); + + /* + * We need to clean up this instance, that means remove it from + * persistence and stopping it. Then finally we'll have to clean it up + * entirely. + */ + if (inst->vri_flags & VARPD_INSTANCE_F_ACTIVATED) { + inst->vri_flags &= ~VARPD_INSTANCE_F_ACTIVATED; + libvarpd_torch_instance(vip, inst); + inst->vri_plugin->vpp_ops->vpo_stop(inst->vri_private); + inst->vri_plugin->vpp_ops->vpo_destroy(inst->vri_private); + inst->vri_private = NULL; + } + mutex_exit(&inst->vri_lock); + + /* Do the full clean up of the instance */ + if (mutex_destroy(&inst->vri_lock) != 0) + libvarpd_panic("failed to destroy instance vri_lock"); + id_free(vip->vdi_idspace, inst->vri_id); + umem_free(inst, sizeof (varpd_instance_t)); +} + +int +libvarpd_instance_activate(varpd_instance_handle_t *ihp) +{ + int ret; + varpd_instance_t *inst = (varpd_instance_t *)ihp; + + mutex_enter(&inst->vri_lock); + + if (inst->vri_flags & VARPD_INSTANCE_F_ACTIVATED) { + ret = EEXIST; + goto out; + } + + if ((ret = inst->vri_plugin->vpp_ops->vpo_start(inst->vri_private)) != + 0) + goto out; + + if ((ret = libvarpd_persist_instance(inst->vri_impl, inst)) != 0) + goto out; + + /* + * If this fails, we don't need to call stop, as the caller should end + * up calling destroy on the instance, which takes care of calling stop + * and destroy. + */ + if ((ret = libvarpd_overlay_associate(inst)) != 0) + goto out; + + inst->vri_flags |= VARPD_INSTANCE_F_ACTIVATED; + +out: + mutex_exit(&inst->vri_lock); + return (ret); +} + +const bunyan_logger_t * +libvarpd_plugin_bunyan(varpd_provider_handle_t *vhp) +{ + varpd_instance_t *inst = (varpd_instance_t *)vhp; + return (inst->vri_impl->vdi_bunyan); +} + +static void +libvarpd_prefork(void) +{ + libvarpd_plugin_prefork(); +} + +static void +libvarpd_postfork(void) +{ + libvarpd_plugin_postfork(); +} + +#pragma init(libvarpd_init) +static void +libvarpd_init(void) +{ + libvarpd_plugin_init(); + if (pthread_atfork(libvarpd_prefork, libvarpd_postfork, + libvarpd_postfork) != 0) + libvarpd_panic("failed to create varpd atfork: %d", errno); +} + +#pragma fini(libvarpd_fini) +static void +libvarpd_fini(void) +{ + libvarpd_plugin_fini(); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd.h b/usr/src/lib/varpd/libvarpd/common/libvarpd.h new file mode 100644 index 0000000000..aaf78e2ca6 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd.h @@ -0,0 +1,77 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_H +#define _LIBVARPD_H + +/* + * varpd interfaces + */ + +#include <sys/types.h> +#include <stdint.h> +#include <sys/mac.h> +#include <libvarpd_client.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __varpd_handle varpd_handle_t; +typedef struct __varpd_prop_handle varpd_prop_handle_t; +typedef struct __varpd_instance_handle varpd_instance_handle_t; + +extern int libvarpd_create(varpd_handle_t **); +extern void libvarpd_destroy(varpd_handle_t *); + +extern int libvarpd_persist_enable(varpd_handle_t *, const char *); +extern int libvarpd_persist_restore(varpd_handle_t *); +extern int libvarpd_persist_disable(varpd_handle_t *); + +extern int libvarpd_instance_create(varpd_handle_t *, datalink_id_t, + const char *, varpd_instance_handle_t **); +extern uint64_t libvarpd_instance_id(varpd_instance_handle_t *); +extern varpd_instance_handle_t *libvarpd_instance_lookup(varpd_handle_t *, + uint64_t); +extern void libvarpd_instance_destroy(varpd_instance_handle_t *); +extern int libvarpd_instance_activate(varpd_instance_handle_t *); + +extern int libvarpd_plugin_load(varpd_handle_t *, const char *); +typedef int (*libvarpd_plugin_walk_f)(varpd_handle_t *, const char *, void *); +extern int libvarpd_plugin_walk(varpd_handle_t *, libvarpd_plugin_walk_f, + void *); + +extern int libvarpd_prop_handle_alloc(varpd_handle_t *, + varpd_instance_handle_t *, varpd_prop_handle_t **); +extern void libvarpd_prop_handle_free(varpd_prop_handle_t *); +extern int libvarpd_prop_nprops(varpd_instance_handle_t *, uint_t *); +extern int libvarpd_prop_info_fill(varpd_prop_handle_t *, uint_t); +extern int libvarpd_prop_info(varpd_prop_handle_t *, const char **, uint_t *, + uint_t *, const void **, uint32_t *, const mac_propval_range_t **); +extern int libvarpd_prop_get(varpd_prop_handle_t *, void *, uint32_t *); +extern int libvarpd_prop_set(varpd_prop_handle_t *, const void *, uint32_t); + +extern int libvarpd_door_server_create(varpd_handle_t *, const char *); +extern void libvarpd_door_server_destroy(varpd_handle_t *); + +extern void libvarpd_overlay_lookup_run(varpd_handle_t *); +extern void libvarpd_overlay_lookup_quiesce(varpd_handle_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_H */ diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_arp.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_arp.c new file mode 100644 index 0000000000..df69207fe0 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_arp.c @@ -0,0 +1,650 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Common routines for implmeenting proxy arp + */ + +#include <sys/types.h> +#include <net/if.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <netinet/udp.h> +#include <netinet/dhcp.h> +#include <libvarpd_impl.h> +#include <sys/vlan.h> +#include <strings.h> +#include <assert.h> + +#define IPV6_VERSION 6 + +typedef struct varpd_arp_query { + int vaq_type; + char vaq_buf[ETHERMAX + VLAN_TAGSZ]; + size_t vaq_bsize; + uint8_t vaq_lookup[ETHERADDRL]; + struct sockaddr_storage vaq_sock; + varpd_instance_t *vaq_inst; + struct ether_arp *vaq_ea; + varpd_query_handle_t *vaq_query; + const overlay_targ_lookup_t *vaq_otl; + ip6_t *vaq_ip6; + nd_neighbor_solicit_t *vaq_ns; +} varpd_arp_query_t; + +typedef struct varpd_dhcp_query { + char vdq_buf[ETHERMAX + VLAN_TAGSZ]; + size_t vdq_bsize; + uint8_t vdq_lookup[ETHERADDRL]; + const overlay_targ_lookup_t *vdq_otl; + varpd_instance_t *vdq_inst; + varpd_query_handle_t *vdq_query; + struct ether_header *vdq_ether; +} varpd_dhcp_query_t; + +static const uint8_t libvarpd_arp_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff }; + +void +libvarpd_plugin_proxy_arp(varpd_provider_handle_t *hdl, + varpd_query_handle_t *vqh, const overlay_targ_lookup_t *otl) +{ + varpd_arp_query_t *vaq; + varpd_instance_t *inst = (varpd_instance_t *)hdl; + struct ether_arp *ea; + struct sockaddr_in *ip; + + vaq = umem_alloc(sizeof (varpd_arp_query_t), UMEM_DEFAULT); + if (vaq == NULL) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + return; + } + vaq->vaq_bsize = sizeof (vaq->vaq_buf); + + if (otl->otl_sap != ETHERTYPE_ARP) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + /* + * An ARP packet should not be very large because it's definited to only + * be allowed to have a single entry at a given time. But our data must + * be at least as large as an ether_arp and our header must be at least + * as large as a standard ethernet header. + */ + if (otl->otl_hdrsize + otl->otl_pktsize > vaq->vaq_bsize || + otl->otl_pktsize < sizeof (struct ether_arp) || + otl->otl_hdrsize < sizeof (struct ether_header)) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (libvarpd_overlay_packet(inst->vri_impl, otl, vaq->vaq_buf, + &vaq->vaq_bsize) != 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (otl->otl_hdrsize + otl->otl_pktsize < vaq->vaq_bsize) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + ea = (void *)((uintptr_t)vaq->vaq_buf + (uintptr_t)otl->otl_hdrsize); + + /* + * Make sure it matches something that we know about. + */ + if (ntohs(ea->ea_hdr.ar_hrd) != ARPHRD_ETHER || + ntohs(ea->ea_hdr.ar_pro) != ETHERTYPE_IP || + ea->ea_hdr.ar_hln != ETHERADDRL || + ea->ea_hdr.ar_pln != sizeof (ea->arp_spa) || + ntohs(ea->ea_hdr.ar_op) != ARPOP_REQUEST) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + /* + * Now that we've verified that our data is sane, see if we're doing a + * gratuitous arp and if so, drop it. Otherwise, we may end up + * triggering duplicate address detection. + */ + if (bcmp(ea->arp_spa, ea->arp_tpa, sizeof (ea->arp_spa)) == 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + bzero(&vaq->vaq_sock, sizeof (struct sockaddr_storage)); + ip = (struct sockaddr_in *)&vaq->vaq_sock; + ip->sin_family = AF_INET; + bcopy(ea->arp_tpa, &ip->sin_addr, sizeof (ea->arp_tpa)); + + vaq->vaq_type = AF_INET; + vaq->vaq_inst = inst; + vaq->vaq_ea = ea; + vaq->vaq_query = vqh; + vaq->vaq_otl = otl; + + if (inst->vri_plugin->vpp_ops->vpo_arp == NULL) + libvarpd_panic("%s plugin asked to do arp, but has no method", + inst->vri_plugin->vpp_name); + + inst->vri_plugin->vpp_ops->vpo_arp(inst->vri_private, + (varpd_arp_handle_t *)vaq, VARPD_QTYPE_ETHERNET, + (struct sockaddr *)ip, vaq->vaq_lookup); +} + +static void +libvarpd_proxy_arp_fini(varpd_arp_query_t *vaq) +{ + struct ether_header *ether; + struct sockaddr_in *ip; + + ip = (struct sockaddr_in *)&vaq->vaq_sock; + /* + * Modify our packet in place for a reply. We need to swap around the + * sender and target addresses. + */ + vaq->vaq_ea->ea_hdr.ar_op = htons(ARPOP_REPLY); + bcopy(vaq->vaq_ea->arp_sha, vaq->vaq_ea->arp_tha, ETHERADDRL); + bcopy(vaq->vaq_lookup, vaq->vaq_ea->arp_sha, ETHERADDRL); + bcopy(vaq->vaq_ea->arp_spa, &ip->sin_addr, + sizeof (vaq->vaq_ea->arp_spa)); + bcopy(vaq->vaq_ea->arp_tpa, vaq->vaq_ea->arp_spa, + sizeof (vaq->vaq_ea->arp_spa)); + bcopy(&ip->sin_addr, vaq->vaq_ea->arp_tpa, + sizeof (vaq->vaq_ea->arp_spa)); + + /* + * Finally go ahead and fix up the mac header and reply to the sender + * explicitly. + */ + ether = (struct ether_header *)vaq->vaq_buf; + bcopy(ðer->ether_shost, ðer->ether_dhost, ETHERADDRL); + bcopy(vaq->vaq_lookup, ðer->ether_shost, ETHERADDRL); + + (void) libvarpd_overlay_inject(vaq->vaq_inst->vri_impl, vaq->vaq_otl, + vaq->vaq_buf, vaq->vaq_bsize); + + libvarpd_plugin_query_reply(vaq->vaq_query, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); +} + +static uint16_t +libvarpd_icmpv6_checksum(const ip6_t *v6hdr, const uint16_t *buf, uint16_t mlen) +{ + int i; + uint16_t *v; + uint32_t sum = 0; + + assert(mlen % 2 == 0); + v = (uint16_t *)&v6hdr->ip6_src; + for (i = 0; i < sizeof (struct in6_addr); i += 2, v++) + sum += *v; + v = (uint16_t *)&v6hdr->ip6_dst; + for (i = 0; i < sizeof (struct in6_addr); i += 2, v++) + sum += *v; + sum += htons(mlen); +#ifdef _BIG_ENDIAN + sum += IPPROTO_ICMPV6; +#else + sum += IPPROTO_ICMPV6 << 8; +#endif /* _BIG_ENDIAN */ + + for (i = 0; i < mlen; i += 2, buf++) + sum += *buf; + + while ((sum >> 16) != 0) + sum = (sum & 0xffff) + (sum >> 16); + + return (sum & 0xffff); +} + +/* + * Proxying NDP is much more involved than proxying ARP. For starters, NDP + * neighbor solicitations are implemented in terms of IPv6 ICMP as opposed to + * its own Ethertype. Therefore, we're going to have to grab a packet if it's a + * multicast packet and then determine if we actually want to do anything with + * it. + */ +void +libvarpd_plugin_proxy_ndp(varpd_provider_handle_t *hdl, + varpd_query_handle_t *vqh, const overlay_targ_lookup_t *otl) +{ + size_t bsize, plen; + varpd_arp_query_t *vaq; + ip6_t *v6hdr; + nd_neighbor_solicit_t *ns; + nd_opt_hdr_t *opt; + struct sockaddr_in6 *s6; + + varpd_instance_t *inst = (varpd_instance_t *)hdl; + uint8_t *eth = NULL; + + vaq = umem_alloc(sizeof (varpd_arp_query_t), UMEM_DEFAULT); + if (vaq == NULL) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + return; + } + vaq->vaq_bsize = sizeof (vaq->vaq_buf); + + if (otl->otl_dstaddr[0] != 0x33 || + otl->otl_dstaddr[1] != 0x33) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + /* + * If we have more than a standard frame size for the ICMP neighbor + * solicitation, drop it. Similarly if there isn't enough data present + * for us, drop it. + */ + if (otl->otl_hdrsize + otl->otl_pktsize > vaq->vaq_bsize) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (otl->otl_pktsize < sizeof (ip6_t) + + sizeof (nd_neighbor_solicit_t)) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (libvarpd_overlay_packet(inst->vri_impl, otl, vaq->vaq_buf, + &vaq->vaq_bsize) != 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + bsize = vaq->vaq_bsize; + bsize -= otl->otl_hdrsize; + assert(bsize > sizeof (ip6_t)); + + v6hdr = (ip6_t *)(vaq->vaq_buf + otl->otl_hdrsize); + if (((v6hdr->ip6_vfc & 0xf0) >> 4) != IPV6_VERSION) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (v6hdr->ip6_nxt != IPPROTO_ICMPV6) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + /* + * In addition to getting these requests on the multicast address for + * node solicitation, we may also end up getting them on a generic + * multicast address due to timeouts or other choices by various OSes. + * We should fairly liberal and accept both, even though the standard + * wants them to a solicitation address. + */ + if (!IN6_IS_ADDR_MC_SOLICITEDNODE(&v6hdr->ip6_dst) && + !IN6_IS_ADDR_MC_LINKLOCAL(&v6hdr->ip6_dst)) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + bsize -= sizeof (ip6_t); + plen = ntohs(v6hdr->ip6_plen); + if (bsize < plen) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + /* + * Now we know that this is an ICMPv6 request targetting the right + * IPv6 multicast prefix. Let's go through and verify that ICMPv6 + * indicates that we have the real thing and ensure that per RFC 4861 + * the target address is not a multicast address. Further, because this + * is a multicast on Ethernet, we must have a source link-layer address. + * + * We should probably enforce that we have a valid ICMP checksum at some + * point. + */ + ns = (nd_neighbor_solicit_t *)(vaq->vaq_buf + otl->otl_hdrsize + + sizeof (ip6_t)); + if (ns->nd_ns_type != ND_NEIGHBOR_SOLICIT && ns->nd_ns_code != 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (IN6_IS_ADDR_MULTICAST(&ns->nd_ns_target) || + IN6_IS_ADDR_V4MAPPED(&ns->nd_ns_target) || + IN6_IS_ADDR_LOOPBACK(&ns->nd_ns_target)) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + plen -= sizeof (nd_neighbor_solicit_t); + opt = (nd_opt_hdr_t *)(ns+1); + while (plen >= sizeof (struct nd_opt_hdr)) { + /* If we have an option with no lenght, that's clear bogus */ + if (opt->nd_opt_len == 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + if (opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR) { + eth = (uint8_t *)((uintptr_t)opt + + sizeof (nd_opt_hdr_t)); + } + plen -= opt->nd_opt_len * 8; + opt = (nd_opt_hdr_t *)((uintptr_t)opt + + opt->nd_opt_len * 8); + } + + if (eth == NULL) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } + + bzero(&vaq->vaq_sock, sizeof (struct sockaddr_storage)); + s6 = (struct sockaddr_in6 *)&vaq->vaq_sock; + s6->sin6_family = AF_INET6; + bcopy(&ns->nd_ns_target, &s6->sin6_addr, sizeof (s6->sin6_addr)); + + if (inst->vri_plugin->vpp_ops->vpo_arp == NULL) + libvarpd_panic("%s plugin asked to do arp, but has no method", + inst->vri_plugin->vpp_name); + + vaq->vaq_type = AF_INET6; + vaq->vaq_inst = inst; + vaq->vaq_ea = NULL; + vaq->vaq_query = vqh; + vaq->vaq_otl = otl; + vaq->vaq_ns = ns; + vaq->vaq_ip6 = v6hdr; + inst->vri_plugin->vpp_ops->vpo_arp(inst->vri_private, + (varpd_arp_handle_t *)vaq, VARPD_QTYPE_ETHERNET, + (struct sockaddr *)s6, vaq->vaq_lookup); +} + +static void +libvarpd_proxy_ndp_fini(varpd_arp_query_t *vaq) +{ + char resp[ETHERMAX + VLAN_TAGSZ]; + struct ether_header *ether; + nd_neighbor_advert_t *na; + nd_opt_hdr_t *opt; + ip6_t *v6hdr; + size_t roff = 0; + + /* + * Now we need to assemble an RA as a response. Unlike with arp, we opt + * to use a new packet just to make things a bit simpler saner here. + */ + v6hdr = vaq->vaq_ip6; + bcopy(vaq->vaq_buf, resp, vaq->vaq_otl->otl_hdrsize); + ether = (struct ether_header *)resp; + bcopy(ðer->ether_shost, ðer->ether_dhost, ETHERADDRL); + bcopy(vaq->vaq_lookup, ðer->ether_shost, ETHERADDRL); + roff += vaq->vaq_otl->otl_hdrsize; + bcopy(v6hdr, resp + roff, sizeof (ip6_t)); + v6hdr = (ip6_t *)(resp + roff); + bcopy(&v6hdr->ip6_src, &v6hdr->ip6_dst, sizeof (struct in6_addr)); + bcopy(&vaq->vaq_ns->nd_ns_target, &v6hdr->ip6_src, + sizeof (struct in6_addr)); + roff += sizeof (ip6_t); + na = (nd_neighbor_advert_t *)(resp + roff); + na->nd_na_type = ND_NEIGHBOR_ADVERT; + na->nd_na_code = 0; + /* + * RFC 4443 defines that we should set the checksum to zero before we + * calculate the checksumat we should set the checksum to zero before we + * calculate it. + */ + na->nd_na_cksum = 0; + /* + * Nota bene, the header <netinet/icmp6.h> has already transformed this + * into the appropriate host order. Don't use htonl. + */ + na->nd_na_flags_reserved = ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE; + bcopy(&vaq->vaq_ns->nd_ns_target, &na->nd_na_target, + sizeof (struct in6_addr)); + roff += sizeof (nd_neighbor_advert_t); + + opt = (nd_opt_hdr_t *)(resp + roff); + opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; + opt->nd_opt_len = 1; + roff += sizeof (nd_opt_hdr_t); + bcopy(vaq->vaq_lookup, resp + roff, ETHERADDRL); + roff += ETHERADDRL; + + /* + * Now that we've filled in the packet, go back and compute the checksum + * and fill in the IPv6 payload size. + */ + v6hdr->ip6_plen = htons(roff - sizeof (ip6_t) - + vaq->vaq_otl->otl_hdrsize); + na->nd_na_cksum = ~libvarpd_icmpv6_checksum(v6hdr, (uint16_t *)na, + ntohs(v6hdr->ip6_plen)) & 0xffff; + + (void) libvarpd_overlay_inject(vaq->vaq_inst->vri_impl, vaq->vaq_otl, + resp, roff); + + libvarpd_plugin_query_reply(vaq->vaq_query, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); +} + +void +libvarpd_plugin_arp_reply(varpd_arp_handle_t *vah, int action) +{ + varpd_arp_query_t *vaq = (varpd_arp_query_t *)vah; + + if (vaq == NULL) + libvarpd_panic("unknown plugin passed invalid " + "varpd_arp_handle_t"); + + if (action == VARPD_LOOKUP_DROP) { + libvarpd_plugin_query_reply(vaq->vaq_query, VARPD_LOOKUP_DROP); + umem_free(vaq, sizeof (varpd_arp_query_t)); + return; + } else if (action != VARPD_LOOKUP_OK) + libvarpd_panic("%s plugin returned invalid action %d", + vaq->vaq_inst->vri_plugin->vpp_name, action); + + switch (vaq->vaq_type) { + case AF_INET: + libvarpd_proxy_arp_fini(vaq); + break; + case AF_INET6: + libvarpd_proxy_ndp_fini(vaq); + break; + default: + libvarpd_panic("encountered unknown vaq_type: %d", + vaq->vaq_type); + } +} + +void +libvarpd_plugin_proxy_dhcp(varpd_provider_handle_t *hdl, + varpd_query_handle_t *vqh, const overlay_targ_lookup_t *otl) +{ + varpd_dhcp_query_t *vdq; + struct ether_header *ether; + struct ip *ip; + struct udphdr *udp; + varpd_instance_t *inst = (varpd_instance_t *)hdl; + + vdq = umem_alloc(sizeof (varpd_dhcp_query_t), UMEM_DEFAULT); + if (vdq == NULL) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + return; + } + vdq->vdq_bsize = sizeof (vdq->vdq_buf); + + if (otl->otl_sap != ETHERTYPE_IP) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + if (bcmp(otl->otl_dstaddr, libvarpd_arp_bcast, ETHERADDRL) != 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + if (otl->otl_hdrsize + otl->otl_pktsize > vdq->vdq_bsize || + otl->otl_pktsize < sizeof (struct ip) + sizeof (struct udphdr) + + sizeof (struct dhcp) || + otl->otl_hdrsize < sizeof (struct ether_header)) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + if (libvarpd_overlay_packet(inst->vri_impl, otl, vdq->vdq_buf, + &vdq->vdq_bsize) != 0) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + if (vdq->vdq_bsize != otl->otl_hdrsize + otl->otl_pktsize) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + ether = (struct ether_header *)vdq->vdq_buf; + ip = (struct ip *)(vdq->vdq_buf + otl->otl_hdrsize); + + if (ip->ip_v != IPVERSION && ip->ip_p != IPPROTO_UDP) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + if (otl->otl_hdrsize + ip->ip_hl * 4 + sizeof (struct udphdr) > + vdq->vdq_bsize) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + udp = (struct udphdr *)(vdq->vdq_buf + otl->otl_hdrsize + + ip->ip_hl * 4); + + if (ntohs(udp->uh_sport) != IPPORT_BOOTPC || + ntohs(udp->uh_dport) != IPPORT_BOOTPS) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } + + vdq->vdq_ether = ether; + vdq->vdq_inst = inst; + vdq->vdq_query = vqh; + vdq->vdq_otl = otl; + + if (inst->vri_plugin->vpp_ops->vpo_dhcp == NULL) + libvarpd_panic("%s plugin asked to do dhcp, but has no method", + inst->vri_plugin->vpp_name); + + inst->vri_plugin->vpp_ops->vpo_dhcp(inst->vri_private, + (varpd_dhcp_handle_t *)vdq, VARPD_QTYPE_ETHERNET, otl, + vdq->vdq_lookup); +} + +void +libvarpd_plugin_dhcp_reply(varpd_dhcp_handle_t *vdh, int action) +{ + varpd_dhcp_query_t *vdq = (varpd_dhcp_query_t *)vdh; + + if (vdq == NULL) + libvarpd_panic("unknown plugin passed invalid " + "varpd_dhcp_handle_t"); + + if (action == VARPD_LOOKUP_DROP) { + libvarpd_plugin_query_reply(vdq->vdq_query, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); + return; + } else if (action != VARPD_LOOKUP_OK) + libvarpd_panic("%s plugin returned invalid action %d", + vdq->vdq_inst->vri_plugin->vpp_name, action); + + bcopy(vdq->vdq_lookup, &vdq->vdq_ether->ether_dhost, ETHERADDRL); + (void) libvarpd_overlay_resend(vdq->vdq_inst->vri_impl, vdq->vdq_otl, + vdq->vdq_buf, vdq->vdq_bsize); + + libvarpd_plugin_query_reply(vdq->vdq_query, VARPD_LOOKUP_DROP); + umem_free(vdq, sizeof (varpd_dhcp_query_t)); +} + +/* + * Inject a gratuitious ARP packet to the specified mac address. + */ +void +libvarpd_inject_arp(varpd_provider_handle_t *vph, const uint16_t vlan, + const uint8_t *srcmac, const struct in_addr *srcip, const uint8_t *dstmac) +{ + char buf[500]; + size_t bsize = 0; + struct ether_arp *ea; + varpd_instance_t *inst = (varpd_instance_t *)vph; + + if (vlan != 0) { + struct ether_vlan_header *eh; + eh = (struct ether_vlan_header *)(buf + bsize); + bsize += sizeof (struct ether_vlan_header); + bcopy(dstmac, &eh->ether_dhost, ETHERADDRL); + bcopy(srcmac, &eh->ether_shost, ETHERADDRL); + eh->ether_tpid = htons(ETHERTYPE_VLAN); + eh->ether_tci = htons(VLAN_TCI(0, ETHER_CFI, vlan)); + eh->ether_type = htons(ETHERTYPE_ARP); + } else { + struct ether_header *eh; + eh = (struct ether_header *)(buf + bsize); + bsize += sizeof (struct ether_header); + bcopy(dstmac, &eh->ether_dhost, ETHERADDRL); + bcopy(srcmac, &eh->ether_shost, ETHERADDRL); + eh->ether_type = htons(ETHERTYPE_ARP); + } + + ea = (struct ether_arp *)(buf + bsize); + bsize += sizeof (struct ether_arp); + ea->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); + ea->ea_hdr.ar_pro = htons(ETHERTYPE_IP); + ea->ea_hdr.ar_hln = ETHERADDRL; + ea->ea_hdr.ar_pln = sizeof (struct in_addr); + ea->ea_hdr.ar_op = htons(ARPOP_REQUEST); + bcopy(srcmac, ea->arp_sha, ETHERADDRL); + bcopy(srcip, ea->arp_spa, sizeof (struct in_addr)); + bcopy(libvarpd_arp_bcast, ea->arp_tha, ETHERADDRL); + bcopy(srcip, ea->arp_tpa, sizeof (struct in_addr)); + + (void) libvarpd_overlay_instance_inject(inst, buf, bsize); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_client.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_client.c new file mode 100644 index 0000000000..18e220259c --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_client.c @@ -0,0 +1,626 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * varpd client interfaces + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <umem.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> +#include <door.h> + +#include <libvarpd_impl.h> + +typedef struct varpd_client { + int vcl_doorfd; +} varpd_client_t; + +typedef struct varpd_client_prop_info { + varpd_client_t *vcprop_client; + uint64_t vcprop_instance; + uint_t vcprop_propid; + uint_t vcprop_type; + uint_t vcprop_prot; + uint32_t vcprop_defsize; + uint32_t vcprop_psize; + char vcprop_name[LIBVARPD_PROP_NAMELEN]; + uint8_t vcprop_default[LIBVARPD_PROP_SIZEMAX]; + uint8_t vcprop_poss[LIBVARPD_PROP_SIZEMAX]; +} varpd_client_prop_info_t; + +static int +libvarpd_c_door_call(varpd_client_t *client, varpd_client_arg_t *argp, + size_t altsize) +{ + int ret; + door_arg_t darg; + + darg.data_ptr = (char *)argp; + darg.desc_ptr = NULL; + darg.desc_num = 0; + darg.rbuf = (char *)argp; + if (altsize != 0) { + darg.data_size = altsize; + darg.rsize = altsize; + } else { + darg.data_size = sizeof (varpd_client_arg_t); + darg.rsize = sizeof (varpd_client_arg_t); + } + + do { + ret = door_call(client->vcl_doorfd, &darg); + } while (ret != 0 && errno == EINTR); + if (ret != 0) { + switch (errno) { + case E2BIG: + case EFAULT: + case EINVAL: + case ENOTSUP: + case EOVERFLOW: + case ENFILE: + libvarpd_panic("unhandalable errno from door_call: %d", + errno); + } + ret = errno; + } + + return (ret); +} + +int +libvarpd_c_create(varpd_client_handle_t **chpp, const char *doorname) +{ + varpd_client_t *client; + + client = umem_alloc(sizeof (varpd_client_t), UMEM_DEFAULT); + if (client == NULL) + return (ENOMEM); + + client->vcl_doorfd = open(doorname, O_RDWR); + if (client->vcl_doorfd < 0) { + int ret = errno; + umem_free(client, sizeof (varpd_client_t)); + return (ret); + } + + *chpp = (varpd_client_handle_t *)client; + return (0); +} + +void +libvarpd_c_destroy(varpd_client_handle_t *chp) +{ + varpd_client_t *client = (varpd_client_t *)chp; + if (close(client->vcl_doorfd) != 0) + libvarpd_panic("failed to close door fd %d: %d", + client->vcl_doorfd, errno); + + umem_free(chp, sizeof (varpd_client_t)); +} + +int +libvarpd_c_instance_create(varpd_client_handle_t *chp, datalink_id_t linkid, + const char *search, uint64_t *cidp) +{ + int ret; + varpd_client_t *client = (varpd_client_t *)chp; + varpd_client_arg_t carg; + varpd_client_create_arg_t *cap = &carg.vca_un.vca_create; + + if (strlen(search) >= LIBVARPD_PROP_NAMELEN) + return (EINVAL); + carg.vca_command = VARPD_CLIENT_CREATE; + carg.vca_errno = 0; + cap->vcca_linkid = linkid; + (void) strlcpy(cap->vcca_plugin, search, LIBVARPD_PROP_NAMELEN); + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + *cidp = cap->vcca_id; + + return (0); +} + +int +libvarpd_c_instance_activate(varpd_client_handle_t *chp, uint64_t cid) +{ + int ret; + varpd_client_t *client = (varpd_client_t *)chp; + varpd_client_arg_t carg; + varpd_client_instance_arg_t *vciap = &carg.vca_un.vca_instance; + + carg.vca_command = VARPD_CLIENT_ACTIVATE; + carg.vca_errno = 0; + vciap->vcia_id = cid; + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + return (0); +} + +int +libvarpd_c_instance_destroy(varpd_client_handle_t *chp, uint64_t cid) +{ + int ret; + varpd_client_t *client = (varpd_client_t *)chp; + varpd_client_arg_t carg; + varpd_client_instance_arg_t *vciap = &carg.vca_un.vca_instance; + + carg.vca_command = VARPD_CLIENT_DESTROY; + carg.vca_errno = 0; + vciap->vcia_id = cid; + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + return (0); +} + +int +libvarpd_c_prop_nprops(varpd_client_handle_t *chp, uint64_t cid, uint_t *nprops) +{ + int ret; + varpd_client_t *client = (varpd_client_t *)chp; + varpd_client_arg_t carg; + varpd_client_nprops_arg_t *vcnap = &carg.vca_un.vca_nprops; + + carg.vca_command = VARPD_CLIENT_NPROPS; + carg.vca_errno = 0; + vcnap->vcna_id = cid; + vcnap->vcna_nprops = 0; + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + *nprops = vcnap->vcna_nprops; + return (0); +} + +int +libvarpd_c_prop_handle_alloc(varpd_client_handle_t *chp, uint64_t cid, + varpd_client_prop_handle_t **phdlp) +{ + varpd_client_prop_info_t *infop; + + infop = umem_alloc(sizeof (varpd_client_prop_info_t), UMEM_DEFAULT); + if (infop == NULL) + return (ENOMEM); + + bzero(infop, sizeof (varpd_client_prop_info_t)); + infop->vcprop_client = (varpd_client_t *)chp; + infop->vcprop_instance = cid; + infop->vcprop_propid = UINT_MAX; + *phdlp = (varpd_client_prop_handle_t *)infop; + return (0); +} + +void +libvarpd_c_prop_handle_free(varpd_client_prop_handle_t *phdl) +{ + umem_free(phdl, sizeof (varpd_client_prop_info_t)); + phdl = NULL; +} + +static void +libvarpd_c_prop_info_from_door(varpd_client_prop_info_t *infop, + const varpd_client_propinfo_arg_t *vcfap) +{ + infop->vcprop_propid = vcfap->vcfa_propid; + infop->vcprop_type = vcfap->vcfa_type; + infop->vcprop_prot = vcfap->vcfa_prot; + infop->vcprop_defsize = vcfap->vcfa_defsize; + infop->vcprop_psize = vcfap->vcfa_psize; + bcopy(vcfap->vcfa_name, infop->vcprop_name, LIBVARPD_PROP_NAMELEN); + bcopy(vcfap->vcfa_default, infop->vcprop_default, + LIBVARPD_PROP_SIZEMAX); + bcopy(vcfap->vcfa_poss, infop->vcprop_poss, LIBVARPD_PROP_SIZEMAX); +} + +int +libvarpd_c_prop_info_fill_by_name(varpd_client_prop_handle_t *phdl, + const char *name) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_propinfo_arg_t *vcfap = &carg.vca_un.vca_info; + varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl; + + if (strlen(name) >= LIBVARPD_PROP_NAMELEN) + return (EINVAL); + bzero(&carg, sizeof (varpd_client_arg_t)); + carg.vca_command = VARPD_CLIENT_PROPINFO; + carg.vca_errno = 0; + vcfap->vcfa_id = infop->vcprop_instance; + vcfap->vcfa_propid = UINT_MAX; + (void) strlcpy(vcfap->vcfa_name, name, LIBVARPD_PROP_NAMELEN); + + ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + libvarpd_c_prop_info_from_door(infop, vcfap); + return (0); +} + +int +libvarpd_c_prop_info_fill(varpd_client_prop_handle_t *phdl, uint_t propid) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_propinfo_arg_t *vcfap = &carg.vca_un.vca_info; + varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl; + + bzero(&carg, sizeof (varpd_client_arg_t)); + carg.vca_command = VARPD_CLIENT_PROPINFO; + carg.vca_errno = 0; + vcfap->vcfa_id = infop->vcprop_instance; + vcfap->vcfa_propid = propid; + + ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + libvarpd_c_prop_info_from_door(infop, vcfap); + return (0); +} + +int +libvarpd_c_prop_info(varpd_client_prop_handle_t *phdl, const char **namep, + uint_t *typep, uint_t *protp, const void **defp, uint32_t *defsizep, + const mac_propval_range_t **possp) +{ + varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl; + if (infop->vcprop_propid == UINT_MAX) + return (EINVAL); + + if (namep != NULL) + *namep = infop->vcprop_name; + if (typep != NULL) + *typep = infop->vcprop_type; + if (protp != NULL) + *protp = infop->vcprop_prot; + if (defp != NULL) + *defp = infop->vcprop_default; + if (defsizep != NULL) + *defsizep = infop->vcprop_defsize; + if (possp != NULL) + *possp = (const mac_propval_range_t *)infop->vcprop_poss; + return (0); +} + +int +libvarpd_c_prop_get(varpd_client_prop_handle_t *phdl, void *buf, uint32_t *len) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_prop_arg_t *vcpap = &carg.vca_un.vca_prop; + varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl; + + if (len == NULL || buf == NULL || infop->vcprop_propid == UINT_MAX) + return (EINVAL); + if (*len < LIBVARPD_PROP_SIZEMAX) + return (EOVERFLOW); + + bzero(&carg, sizeof (varpd_client_arg_t)); + carg.vca_command = VARPD_CLIENT_GETPROP; + carg.vca_errno = 0; + vcpap->vcpa_id = infop->vcprop_instance; + vcpap->vcpa_propid = infop->vcprop_propid; + + ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + /* + * If the buffer size is too large then something odd has certainly + * happened here, it means that varpd has gone rogue. In such a case we + * return a rather odd errror, though we don't believe that this should + * generally happen. + */ + if (vcpap->vcpa_bufsize > LIBVARPD_PROP_SIZEMAX) + return (E2BIG); + + bcopy(vcpap->vcpa_buf, buf, vcpap->vcpa_bufsize); + *len = vcpap->vcpa_bufsize; + return (0); +} + +int +libvarpd_c_prop_set(varpd_client_prop_handle_t *phdl, const void *buf, + uint32_t len) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_prop_arg_t *vcpap = &carg.vca_un.vca_prop; + varpd_client_prop_info_t *infop = (varpd_client_prop_info_t *)phdl; + + if (len == 0 || buf == NULL || infop->vcprop_propid == UINT_MAX) + return (EINVAL); + if (len > LIBVARPD_PROP_SIZEMAX) + return (EOVERFLOW); + + carg.vca_command = VARPD_CLIENT_SETPROP; + carg.vca_errno = 0; + vcpap->vcpa_id = infop->vcprop_instance; + vcpap->vcpa_propid = infop->vcprop_propid; + vcpap->vcpa_bufsize = len; + bcopy(buf, vcpap->vcpa_buf, len); + + ret = libvarpd_c_door_call(infop->vcprop_client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + return (0); +} + +int +libvarpd_c_instance_lookup(varpd_client_handle_t *chp, datalink_id_t linkid, + uint64_t *instp) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_lookup_arg_t *vclap = &carg.vca_un.vca_lookup; + varpd_client_t *client = (varpd_client_t *)chp; + + carg.vca_command = VARPD_CLIENT_LOOKUP; + carg.vca_errno = 0; + vclap->vcla_linkid = linkid; + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + if (instp != NULL) + *instp = vclap->vcla_id; + + return (0); +} + +int +libvarpd_c_instance_target_mode(varpd_client_handle_t *chp, uint64_t cid, + uint_t *dtype, uint_t *mtype) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_target_mode_arg_t *vctmap = &carg.vca_un.vca_mode; + varpd_client_t *client = (varpd_client_t *)chp; + + carg.vca_command = VARPD_CLIENT_TARGET_MODE; + carg.vca_errno = 0; + vctmap->vtma_id = cid; + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + if (ret == 0) { + if (mtype != NULL) + *mtype = vctmap->vtma_mode; + if (dtype != NULL) + *dtype = vctmap->vtma_dest; + } + + return (ret); +} + +int +libvarpd_c_instance_cache_flush(varpd_client_handle_t *chp, uint64_t cid) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache; + varpd_client_t *client = (varpd_client_t *)chp; + + carg.vca_command = VARPD_CLIENT_CACHE_FLUSH; + carg.vca_errno = 0; + + vctcap->vtca_id = cid; + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + return (0); +} + +int +libvarpd_c_instance_cache_delete(varpd_client_handle_t *chp, uint64_t cid, + const struct ether_addr *key) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache; + varpd_client_t *client = (varpd_client_t *)chp; + + if (key == NULL) + return (EINVAL); + + carg.vca_command = VARPD_CLIENT_CACHE_DELETE; + carg.vca_errno = 0; + vctcap->vtca_id = cid; + bcopy(key, vctcap->vtca_key, ETHERADDRL); + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + return (0); +} + +int +libvarpd_c_instance_cache_get(varpd_client_handle_t *chp, uint64_t cid, + const struct ether_addr *key, varpd_client_cache_entry_t *entry) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache; + varpd_client_t *client = (varpd_client_t *)chp; + + if (key == NULL || entry == NULL) + return (EINVAL); + + carg.vca_command = VARPD_CLIENT_CACHE_GET; + carg.vca_errno = 0; + vctcap->vtca_id = cid; + bcopy(key, vctcap->vtca_key, ETHERADDRL); + bzero(&vctcap->vtca_entry, sizeof (varpd_client_cache_entry_t)); + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + bcopy(&vctcap->vtca_entry, entry, sizeof (varpd_client_cache_entry_t)); + return (0); +} + +int +libvarpd_c_instance_cache_set(varpd_client_handle_t *chp, uint64_t cid, + const struct ether_addr *key, const varpd_client_cache_entry_t *entry) +{ + int ret; + varpd_client_arg_t carg; + varpd_client_target_cache_arg_t *vctcap = &carg.vca_un.vca_cache; + varpd_client_t *client = (varpd_client_t *)chp; + + if (key == NULL || entry == NULL) + return (EINVAL); + + carg.vca_command = VARPD_CLIENT_CACHE_SET; + carg.vca_errno = 0; + vctcap->vtca_id = cid; + bcopy(key, vctcap->vtca_key, ETHERADDRL); + bcopy(entry, &vctcap->vtca_entry, sizeof (varpd_client_cache_entry_t)); + + ret = libvarpd_c_door_call(client, &carg, 0); + if (ret != 0) + return (ret); + + if (carg.vca_errno != 0) + return (carg.vca_errno); + + return (0); +} + +int +libvarpd_c_instance_cache_walk(varpd_client_handle_t *chp, uint64_t cid, + varpd_client_cache_f func, void *arg) +{ + int ret = 0; + size_t bufsize = sizeof (varpd_client_arg_t) + + 100 * sizeof (varpd_client_cache_entry_t); + varpd_client_t *client = (varpd_client_t *)chp; + varpd_client_arg_t *cargp; + varpd_client_target_walk_arg_t *vctwap; + + /* + * Because the number of entries involved in a walk may be large, we + * dynamically allocate a number of queries to make at a single time. + * This also means that the average door request doesn't inflate by the + * number of entries we want. For now, let's always grab 100 entries in + * a request. + */ + cargp = umem_zalloc(bufsize, UMEM_DEFAULT); + if (cargp == NULL) + return (errno); + vctwap = &cargp->vca_un.vca_walk; + for (;;) { + int i; + + cargp->vca_command = VARPD_CLIENT_CACHE_WALK; + cargp->vca_errno = 0; + vctwap->vtcw_id = cid; + vctwap->vtcw_count = 100; + + ret = libvarpd_c_door_call(client, cargp, bufsize); + if (ret != 0) + break; + + if (cargp->vca_errno != 0) { + ret = cargp->vca_errno; + break; + } + + if (vctwap->vtcw_count == 0) { + ret = 0; + break; + } + + for (i = 0; i < vctwap->vtcw_count; i++) { + varpd_client_cache_entry_t ent; + + ent.vcp_flags = vctwap->vtcw_ents[i].otce_flags; + bcopy(vctwap->vtcw_ents[i].otce_dest.otp_mac, + &ent.vcp_mac, ETHERADDRL); + ent.vcp_ip = vctwap->vtcw_ents[i].otce_dest.otp_ip; + ent.vcp_port = vctwap->vtcw_ents[i].otce_dest.otp_port; + ret = func(chp, cid, + (struct ether_addr *)vctwap->vtcw_ents[i].otce_mac, + &ent, arg); + if (ret != 0) { + ret = 0; + goto done; + } + } + } + +done: + umem_free(cargp, bufsize); + return (ret); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_client.h b/usr/src/lib/varpd/libvarpd/common/libvarpd_client.h new file mode 100644 index 0000000000..459711b385 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_client.h @@ -0,0 +1,92 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_CLIENT_H +#define _LIBVARPD_CLIENT_H + +/* + * varpd interfaces + */ + +#include <sys/types.h> +#include <stdint.h> +#include <sys/mac.h> +#include <sys/overlay_target.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __varpd_client_handle varpd_client_handle_t; +typedef struct __varpd_client_prop_handle varpd_client_prop_handle_t; + +typedef struct varpd_client_cache_entry { + struct ether_addr vcp_mac; + uint16_t vcp_flags; + struct in6_addr vcp_ip; + uint16_t vcp_port; +} varpd_client_cache_entry_t; + +/* + * We just use the values from the kernel for now. + */ +#define LIBVARPD_PROP_SIZEMAX OVERLAY_PROP_SIZEMAX +#define LIBVARPD_PROP_NAMELEN OVERLAY_PROP_NAMELEN + +extern int libvarpd_c_create(varpd_client_handle_t **, const char *); +extern void libvarpd_c_destroy(varpd_client_handle_t *); +extern int libvarpd_c_instance_create(varpd_client_handle_t *, datalink_id_t, + const char *, uint64_t *); +extern int libvarpd_c_instance_activate(varpd_client_handle_t *, uint64_t); +extern int libvarpd_c_instance_destroy(varpd_client_handle_t *, uint64_t); + +extern int libvarpd_c_prop_nprops(varpd_client_handle_t *, uint64_t, uint_t *); +extern int libvarpd_c_prop_handle_alloc(varpd_client_handle_t *, uint64_t, + varpd_client_prop_handle_t **); +extern void libvarpd_c_prop_handle_free(varpd_client_prop_handle_t *); +extern int libvarpd_c_prop_info_fill(varpd_client_prop_handle_t *, uint_t); +extern int libvarpd_c_prop_info_fill_by_name(varpd_client_prop_handle_t *, + const char *); +extern int libvarpd_c_prop_info(varpd_client_prop_handle_t *, const char **, + uint_t *, uint_t *, const void **, uint32_t *, + const mac_propval_range_t **); +extern int libvarpd_c_prop_get(varpd_client_prop_handle_t *, void *, + uint32_t *); +extern int libvarpd_c_prop_set(varpd_client_prop_handle_t *, const void *, + uint32_t); + +extern int libvarpd_c_instance_lookup(varpd_client_handle_t *, datalink_id_t, + uint64_t *); +extern int libvarpd_c_instance_target_mode(varpd_client_handle_t *, uint64_t, + uint_t *, uint_t *); +extern int libvarpd_c_instance_cache_flush(varpd_client_handle_t *, uint64_t); +extern int libvarpd_c_instance_cache_delete(varpd_client_handle_t *, uint64_t, + const struct ether_addr *); +extern int libvarpd_c_instance_cache_get(varpd_client_handle_t *, uint64_t, + const struct ether_addr *, varpd_client_cache_entry_t *); +extern int libvarpd_c_instance_cache_set(varpd_client_handle_t *, uint64_t, + const struct ether_addr *, const varpd_client_cache_entry_t *); + +typedef int (*varpd_client_cache_f)(varpd_client_handle_t *, uint64_t, + const struct ether_addr *, const varpd_client_cache_entry_t *, void *); +extern int libvarpd_c_instance_cache_walk(varpd_client_handle_t *, uint64_t, + varpd_client_cache_f, void *); + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_CLIENT_H */ diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_door.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_door.c new file mode 100644 index 0000000000..f684e031a8 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_door.c @@ -0,0 +1,469 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * varpd door server logic + */ + +#include <door.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stropts.h> +#include <stdlib.h> +#include <strings.h> +#include <priv.h> +#include <libvarpd_impl.h> + +typedef int (libvarpd_door_f)(varpd_impl_t *, varpd_client_arg_t *, ucred_t *); + +static boolean_t +libvarpd_door_privileged(ucred_t *credp) +{ + const priv_set_t *ps; + + ps = ucred_getprivset(credp, PRIV_EFFECTIVE); + if (ps == NULL) + return (B_FALSE); + + return (priv_ismember(ps, PRIV_SYS_NET_CONFIG)); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_create(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + int ret; + varpd_instance_handle_t *ihdl; + varpd_client_create_arg_t *vccap = &vcap->vca_un.vca_create; + + vccap->vcca_plugin[LIBVARPD_PROP_NAMELEN-1] = '\0'; + ret = libvarpd_instance_create((varpd_handle_t *)vip, + vccap->vcca_linkid, vccap->vcca_plugin, &ihdl); + if (ret == 0) + vccap->vcca_id = libvarpd_instance_id(ihdl); + + return (ret); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_activate(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_instance_arg_t *vciap = &vcap->vca_un.vca_instance; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vciap->vcia_id); + if (ihp == NULL) + return (ENOENT); + return (libvarpd_instance_activate(ihp)); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_destroy(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_instance_arg_t *vciap = &vcap->vca_un.vca_instance; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vciap->vcia_id); + if (ihp == NULL) + return (ENOENT); + libvarpd_instance_destroy(ihp); + return (0); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_nprops(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_nprops_arg_t *vcnap = &vcap->vca_un.vca_nprops; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcnap->vcna_id); + if (ihp == NULL) + return (ENOENT); + + return (libvarpd_prop_nprops(ihp, &vcnap->vcna_nprops)); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_propinfo(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + int ret; + varpd_instance_handle_t *ihp; + varpd_prop_handle_t *phdl; + varpd_client_propinfo_arg_t *vcfap = &vcap->vca_un.vca_info; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcfap->vcfa_id); + if (ihp == NULL) + return (ENOENT); + ret = libvarpd_prop_handle_alloc((varpd_handle_t *)vip, ihp, &phdl); + if (ret != 0) + return (ret); + + if (vcfap->vcfa_propid != UINT_MAX) { + ret = libvarpd_prop_info_fill(phdl, vcfap->vcfa_propid); + if (ret != 0) { + libvarpd_prop_handle_free(phdl); + return (ret); + } + } else { + uint_t i, nprop; + const char *name; + + vcfap->vcfa_name[LIBVARPD_PROP_NAMELEN-1] = '\0'; + ret = libvarpd_prop_nprops(ihp, &nprop); + if (ret != 0) { + libvarpd_prop_handle_free(phdl); + return (ret); + } + for (i = 0; i < nprop; i++) { + ret = libvarpd_prop_info_fill(phdl, i); + if (ret != 0) { + libvarpd_prop_handle_free(phdl); + return (ret); + } + ret = libvarpd_prop_info(phdl, &name, NULL, NULL, NULL, + NULL, NULL); + if (ret != 0) { + libvarpd_prop_handle_free(phdl); + return (ret); + } + if (strcmp(vcfap->vcfa_name, name) == 0) + break; + } + + if (i == nprop) { + libvarpd_prop_handle_free(phdl); + return (ENOENT); + } + vcfap->vcfa_propid = i; + } + libvarpd_prop_door_convert(phdl, vcfap); + libvarpd_prop_handle_free(phdl); + return (0); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_getprop(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + int ret; + uint32_t size; + varpd_instance_handle_t *ihp; + varpd_prop_handle_t *phdl; + varpd_client_prop_arg_t *vcpap = &vcap->vca_un.vca_prop; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcpap->vcpa_id); + if (ihp == NULL) + return (ENOENT); + ret = libvarpd_prop_handle_alloc((varpd_handle_t *)vip, ihp, &phdl); + if (ret != 0) + return (ret); + + ret = libvarpd_prop_info_fill(phdl, vcpap->vcpa_propid); + if (ret != 0) { + libvarpd_prop_handle_free(phdl); + return (ret); + } + + ret = libvarpd_prop_get(phdl, vcpap->vcpa_buf, &size); + if (ret == 0) + vcpap->vcpa_bufsize = size; + libvarpd_prop_handle_free(phdl); + return (0); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_setprop(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + int ret; + varpd_instance_handle_t *ihp; + varpd_prop_handle_t *phdl; + varpd_client_prop_arg_t *vcpap = &vcap->vca_un.vca_prop; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcpap->vcpa_id); + if (ihp == NULL) + return (ENOENT); + ret = libvarpd_prop_handle_alloc((varpd_handle_t *)vip, ihp, &phdl); + if (ret != 0) + return (ret); + + ret = libvarpd_prop_info_fill(phdl, vcpap->vcpa_propid); + if (ret != 0) { + libvarpd_prop_handle_free(phdl); + return (ret); + } + + ret = libvarpd_prop_set(phdl, vcpap->vcpa_buf, vcpap->vcpa_bufsize); + libvarpd_prop_handle_free(phdl); + return (ret); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_lookup(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_t *inst; + varpd_client_lookup_arg_t *vclap = &vcap->vca_un.vca_lookup; + + inst = libvarpd_instance_lookup_by_dlid(vip, vclap->vcla_linkid); + if (inst == NULL) + return (ENOENT); + + vclap->vcla_id = inst->vri_id; + return (0); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_target(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_instance_t *inst; + varpd_client_target_mode_arg_t *vtmap = &vcap->vca_un.vca_mode; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtmap->vtma_id); + if (ihp == NULL) + return (ENOENT); + inst = (varpd_instance_t *)ihp; + vtmap->vtma_dest = inst->vri_dest; + vtmap->vtma_mode = inst->vri_mode; + return (0); +} + +static int +libvarpd_door_f_flush(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache; + + if (libvarpd_door_privileged(credp) == B_FALSE) + return (EPERM); + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id); + if (ihp == NULL) + return (ENOENT); + return (libvarpd_overlay_cache_flush((varpd_instance_t *)ihp)); +} + +static int +libvarpd_door_f_delete(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache; + + if (libvarpd_door_privileged(credp) == B_FALSE) + return (EPERM); + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id); + if (ihp == NULL) + return (ENOENT); + return (libvarpd_overlay_cache_delete((varpd_instance_t *)ihp, + vtcap->vtca_key)); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_get(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id); + if (ihp == NULL) + return (ENOENT); + return (libvarpd_overlay_cache_get((varpd_instance_t *)ihp, + vtcap->vtca_key, &vtcap->vtca_entry)); +} + +static int +libvarpd_door_f_set(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache; + + if (libvarpd_door_privileged(credp) == B_FALSE) + return (EPERM); + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id); + if (ihp == NULL) + return (ENOENT); + + return (libvarpd_overlay_cache_set((varpd_instance_t *)ihp, + vtcap->vtca_key, &vtcap->vtca_entry)); +} + +/* ARGSUSED */ +static int +libvarpd_door_f_walk(varpd_impl_t *vip, varpd_client_arg_t *vcap, + ucred_t *credp) +{ + varpd_instance_handle_t *ihp; + varpd_client_target_walk_arg_t *vctwp = &vcap->vca_un.vca_walk; + + ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vctwp->vtcw_id); + if (ihp == NULL) + return (ENOENT); + + return (libvarpd_overlay_cache_walk_fill((varpd_instance_t *)ihp, + &vctwp->vtcw_marker, &vctwp->vtcw_count, vctwp->vtcw_ents)); +} + +static libvarpd_door_f *libvarpd_door_table[] = { + libvarpd_door_f_create, + libvarpd_door_f_activate, + libvarpd_door_f_destroy, + libvarpd_door_f_nprops, + libvarpd_door_f_propinfo, + libvarpd_door_f_getprop, + libvarpd_door_f_setprop, + libvarpd_door_f_lookup, + libvarpd_door_f_target, + libvarpd_door_f_flush, + libvarpd_door_f_delete, + libvarpd_door_f_get, + libvarpd_door_f_set, + libvarpd_door_f_walk +}; + +/* ARGSUSED */ +static void +libvarpd_door_server(void *cookie, char *argp, size_t argsz, door_desc_t *dp, + uint_t ndesc) +{ + int ret; + varpd_client_eresp_t err; + ucred_t *credp = NULL; + varpd_impl_t *vip = cookie; + varpd_client_arg_t *vcap = (varpd_client_arg_t *)argp; + + err.vce_command = VARPD_CLIENT_INVALID; + if (argsz < sizeof (varpd_client_arg_t)) { + err.vce_errno = EINVAL; + goto errout; + } + + if ((ret = door_ucred(&credp)) != 0) { + err.vce_errno = ret; + goto errout; + } + + if (vcap->vca_command == VARPD_CLIENT_INVALID || + vcap->vca_command >= VARPD_CLIENT_MAX) { + err.vce_errno = EINVAL; + goto errout; + } + + vcap->vca_errno = 0; + ret = libvarpd_door_table[vcap->vca_command - 1](vip, vcap, credp); + if (ret != 0) + vcap->vca_errno = ret; + + ucred_free(credp); + (void) door_return(argp, argsz, NULL, 0); + return; + +errout: + ucred_free(credp); + (void) door_return((char *)&err, sizeof (err), NULL, 0); +} + +int +libvarpd_door_server_create(varpd_handle_t *vhp, const char *path) +{ + int fd, ret; + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + mutex_enter(&vip->vdi_lock); + if (vip->vdi_doorfd >= 0) { + mutex_exit(&vip->vdi_lock); + return (EEXIST); + } + + vip->vdi_doorfd = door_create(libvarpd_door_server, vip, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL); + if (vip->vdi_doorfd == -1) { + mutex_exit(&vip->vdi_lock); + return (errno); + } + + if ((fd = open(path, O_CREAT | O_RDWR, 0666)) == -1) { + ret = errno; + if (door_revoke(vip->vdi_doorfd) != 0) + libvarpd_panic("failed to revoke door: %d", + errno); + mutex_exit(&vip->vdi_lock); + return (errno); + } + + if (fchown(fd, UID_NETADM, GID_NETADM) != 0) { + ret = errno; + if (door_revoke(vip->vdi_doorfd) != 0) + libvarpd_panic("failed to revoke door: %d", + errno); + mutex_exit(&vip->vdi_lock); + return (ret); + } + + if (close(fd) != 0) + libvarpd_panic("failed to close door fd %d: %d", + fd, errno); + (void) fdetach(path); + if (fattach(vip->vdi_doorfd, path) != 0) { + ret = errno; + if (door_revoke(vip->vdi_doorfd) != 0) + libvarpd_panic("failed to revoke door: %d", + errno); + mutex_exit(&vip->vdi_lock); + return (ret); + } + + mutex_exit(&vip->vdi_lock); + return (0); +} + +void +libvarpd_door_server_destroy(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + mutex_enter(&vip->vdi_lock); + if (vip->vdi_doorfd != 0) { + if (door_revoke(vip->vdi_doorfd) != 0) + libvarpd_panic("failed to revoke door: %d", + errno); + vip->vdi_doorfd = -1; + } + mutex_exit(&vip->vdi_lock); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h b/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h new file mode 100644 index 0000000000..60f0dc5fff --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h @@ -0,0 +1,249 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_IMPL_H +#define _LIBVARPD_IMPL_H + +/* + * varpd internal interfaces + */ + +#include <libvarpd.h> +#include <libvarpd_provider.h> +#include <sys/avl.h> +#include <thread.h> +#include <synch.h> +#include <limits.h> +#include <libidspace.h> +#include <umem.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBVARPD_ID_MIN 1 +#define LIBVARPD_ID_MAX INT32_MAX + +typedef struct varpd_plugin { + avl_node_t vpp_node; + const char *vpp_name; + overlay_target_mode_t vpp_mode; + const varpd_plugin_ops_t *vpp_ops; + mutex_t vpp_lock; + uint_t vpp_active; +} varpd_plugin_t; + +typedef struct varpd_impl { + mutex_t vdi_lock; + rwlock_t vdi_pfdlock; + avl_tree_t vdi_plugins; /* vdi_lock */ + avl_tree_t vdi_instances; /* vdi_lock */ + avl_tree_t vdi_linstances; /* vdi_lock */ + id_space_t *vdi_idspace; /* RO */ + umem_cache_t *vdi_qcache; /* RO */ + bunyan_logger_t *vdi_bunyan; /* RO */ + int vdi_overlayfd; /* RO */ + int vdi_doorfd; /* vdi_lock */ + int vdi_persistfd; /* vdi_plock */ + cond_t vdi_lthr_cv; /* vdi_lock */ + boolean_t vdi_lthr_quiesce; /* vdi_lock */ + uint_t vdi_lthr_count; /* vdi_lock */ +} varpd_impl_t; + +typedef enum varpd_instance_flags { + VARPD_INSTANCE_F_ACTIVATED = 0x01 +} varpd_instance_flags_t; + +typedef struct varpd_instance { + avl_node_t vri_inode; + avl_node_t vri_lnode; + uint64_t vri_id; /* RO */ + uint64_t vri_vnetid; /* RO */ + datalink_id_t vri_linkid; /* RO */ + overlay_target_mode_t vri_mode; /* RO */ + overlay_plugin_dest_t vri_dest; /* RO */ + varpd_impl_t *vri_impl; /* RO */ + varpd_plugin_t *vri_plugin; /* RO */ + void *vri_private; /* RO */ + mutex_t vri_lock; + varpd_instance_flags_t vri_flags; /* vri_lock */ +} varpd_instance_t; + +typedef struct varpd_query { + overlay_targ_lookup_t vq_lookup; + overlay_targ_resp_t vq_response; + varpd_instance_t *vq_instance; +} varpd_query_t; + +typedef struct varpd_client_create_arg { + datalink_id_t vcca_linkid; + uint64_t vcca_id; + char vcca_plugin[LIBVARPD_PROP_NAMELEN]; +} varpd_client_create_arg_t; + +typedef struct varpd_client_instance_arg { + uint64_t vcia_id; +} varpd_client_instance_arg_t; + +typedef struct varpd_client_nprops_arg { + uint64_t vcna_id; + uint_t vcna_nprops; + uint8_t vcna_pad[4]; +} varpd_client_nprops_arg_t; + +typedef struct varpd_client_propinfo_arg { + uint64_t vcfa_id; + uint_t vcfa_propid; + uint_t vcfa_type; + uint_t vcfa_prot; + uint32_t vcfa_defsize; + uint32_t vcfa_psize; + uint8_t vcfa_pad[4]; + char vcfa_name[LIBVARPD_PROP_NAMELEN]; + uint8_t vcfa_default[LIBVARPD_PROP_SIZEMAX]; + uint8_t vcfa_poss[LIBVARPD_PROP_SIZEMAX]; +} varpd_client_propinfo_arg_t; + +typedef struct varpd_client_prop_arg { + uint64_t vcpa_id; + uint_t vcpa_propid; + uint8_t vcpa_buf[LIBVARPD_PROP_SIZEMAX]; + size_t vcpa_bufsize; +} varpd_client_prop_arg_t; + +typedef struct varpd_client_lookup_arg { + datalink_id_t vcla_linkid; + uint32_t vcla_pad; + uint64_t vcla_id; +} varpd_client_lookup_arg_t; + +typedef struct varpd_client_target_mode_arg { + uint64_t vtma_id; + uint32_t vtma_dest; + uint32_t vtma_mode; +} varpd_client_target_mode_arg_t; + +typedef struct varpd_client_target_cache_arg { + uint64_t vtca_id; + uint8_t vtca_key[ETHERADDRL]; + uint8_t vtca_pad[2]; + varpd_client_cache_entry_t vtca_entry; +} varpd_client_target_cache_arg_t; + +typedef struct varpd_client_target_walk_arg { + uint64_t vtcw_id; + uint64_t vtcw_marker; + uint64_t vtcw_count; + overlay_targ_cache_entry_t vtcw_ents[]; +} varpd_client_target_walk_arg_t; + +typedef enum varpd_client_command { + VARPD_CLIENT_INVALID = 0x0, + VARPD_CLIENT_CREATE, + VARPD_CLIENT_ACTIVATE, + VARPD_CLIENT_DESTROY, + VARPD_CLIENT_NPROPS, + VARPD_CLIENT_PROPINFO, + VARPD_CLIENT_GETPROP, + VARPD_CLIENT_SETPROP, + VARPD_CLIENT_LOOKUP, + VARPD_CLIENT_TARGET_MODE, + VARPD_CLIENT_CACHE_FLUSH, + VARPD_CLIENT_CACHE_DELETE, + VARPD_CLIENT_CACHE_GET, + VARPD_CLIENT_CACHE_SET, + VARPD_CLIENT_CACHE_WALK, + VARPD_CLIENT_MAX +} varpd_client_command_t; + +typedef struct varpd_client_arg { + uint_t vca_command; + uint_t vca_errno; + union { + varpd_client_create_arg_t vca_create; + varpd_client_instance_arg_t vca_instance; + varpd_client_nprops_arg_t vca_nprops; + varpd_client_propinfo_arg_t vca_info; + varpd_client_prop_arg_t vca_prop; + varpd_client_lookup_arg_t vca_lookup; + varpd_client_target_mode_arg_t vca_mode; + varpd_client_target_cache_arg_t vca_cache; + varpd_client_target_walk_arg_t vca_walk; + } vca_un; +} varpd_client_arg_t; + +typedef struct varpd_client_eresp { + uint_t vce_command; + uint_t vce_errno; +} varpd_client_eresp_t; + +extern void libvarpd_plugin_init(void); +extern void libvarpd_plugin_prefork(void); +extern void libvarpd_plugin_postfork(void); +extern void libvarpd_plugin_fini(void); +extern int libvarpd_plugin_comparator(const void *, const void *); +extern varpd_plugin_t *libvarpd_plugin_lookup(varpd_impl_t *, const char *); + +extern varpd_instance_t *libvarpd_instance_lookup_by_dlid(varpd_impl_t *, + datalink_id_t); + +extern void libvarpd_prop_door_convert(const varpd_prop_handle_t *, + varpd_client_propinfo_arg_t *); + +extern const char *libvarpd_isaext(void); +typedef int (*libvarpd_dirwalk_f)(varpd_impl_t *, const char *, void *); +extern int libvarpd_dirwalk(varpd_impl_t *, const char *, const char *, + libvarpd_dirwalk_f, void *); + +extern int libvarpd_overlay_init(varpd_impl_t *); +extern void libvarpd_overlay_fini(varpd_impl_t *); +extern int libvarpd_overlay_info(varpd_impl_t *, datalink_id_t, + overlay_plugin_dest_t *, uint64_t *, uint64_t *); +extern int libvarpd_overlay_associate(varpd_instance_t *); +extern int libvarpd_overlay_disassociate(varpd_instance_t *); +extern int libvarpd_overlay_degrade(varpd_instance_t *, const char *); +extern int libvarpd_overlay_degrade_datalink(varpd_impl_t *, datalink_id_t, + const char *); +extern int libvarpd_overlay_restore(varpd_instance_t *); +extern int libvarpd_overlay_packet(varpd_impl_t *, + const overlay_targ_lookup_t *, void *, size_t *); +extern int libvarpd_overlay_inject(varpd_impl_t *, + const overlay_targ_lookup_t *, void *, size_t); +extern int libvarpd_overlay_instance_inject(varpd_instance_t *, void *, size_t); +extern int libvarpd_overlay_resend(varpd_impl_t *, + const overlay_targ_lookup_t *, void *, size_t); +typedef int (*libvarpd_overlay_iter_f)(varpd_impl_t *, datalink_id_t, void *); +extern int libvarpd_overlay_iter(varpd_impl_t *, libvarpd_overlay_iter_f, + void *); +extern int libvarpd_overlay_cache_flush(varpd_instance_t *); +extern int libvarpd_overlay_cache_delete(varpd_instance_t *, const uint8_t *); +extern int libvarpd_overlay_cache_delete(varpd_instance_t *, const uint8_t *); +extern int libvarpd_overlay_cache_get(varpd_instance_t *, const uint8_t *, + varpd_client_cache_entry_t *); +extern int libvarpd_overlay_cache_set(varpd_instance_t *, const uint8_t *, + const varpd_client_cache_entry_t *); +extern int libvarpd_overlay_cache_walk_fill(varpd_instance_t *, uint64_t *, + uint64_t *, overlay_targ_cache_entry_t *); + +extern void libvarpd_persist_init(varpd_impl_t *); +extern void libvarpd_persist_fini(varpd_impl_t *); +extern int libvarpd_persist_instance(varpd_impl_t *, varpd_instance_t *); +extern void libvarpd_torch_instance(varpd_impl_t *, varpd_instance_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_IMPL_H */ diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c new file mode 100644 index 0000000000..124e3c5791 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c @@ -0,0 +1,586 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Interactions with /dev/overlay + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> +#include <stropts.h> +#include <strings.h> +#include <umem.h> + +#include <libvarpd_impl.h> +#include <sys/overlay_target.h> + +#define OVERLAY_PATH "/dev/overlay" + +int +libvarpd_overlay_init(varpd_impl_t *vip) +{ + vip->vdi_overlayfd = open(OVERLAY_PATH, O_RDWR | O_EXCL); + if (vip->vdi_overlayfd == -1) + return (errno); + return (0); +} + +void +libvarpd_overlay_fini(varpd_impl_t *vip) +{ + assert(vip->vdi_overlayfd > 0); + if (close(vip->vdi_overlayfd) != 0) + libvarpd_panic("failed to close /dev/overlay fd %d: %d", + vip->vdi_overlayfd, errno); +} + +int +libvarpd_overlay_info(varpd_impl_t *vip, datalink_id_t linkid, + overlay_plugin_dest_t *destp, uint64_t *flags, uint64_t *vnetid) +{ + overlay_targ_info_t oti; + + oti.oti_linkid = linkid; + if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_INFO, &oti) != 0) + return (errno); + + if (destp != NULL) + *destp = oti.oti_needs; + if (flags != NULL) + *flags = oti.oti_flags; + if (vnetid != NULL) + *vnetid = oti.oti_vnetid; + return (0); +} + +int +libvarpd_overlay_associate(varpd_instance_t *inst) +{ + overlay_targ_associate_t ota; + varpd_impl_t *vip = inst->vri_impl; + + bzero(&ota, sizeof (overlay_targ_associate_t)); + ota.ota_linkid = inst->vri_linkid; + ota.ota_mode = inst->vri_mode; + ota.ota_id = inst->vri_id; + ota.ota_provides = inst->vri_dest; + + if (ota.ota_mode == OVERLAY_TARGET_POINT) { + int ret; + ret = inst->vri_plugin->vpp_ops->vpo_default(inst->vri_private, + &ota.ota_point); + if (ret != VARPD_LOOKUP_OK) + return (ret); + } + + if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_ASSOCIATE, &ota) != 0) + return (errno); + + return (0); +} + +int +libvarpd_overlay_disassociate(varpd_instance_t *inst) +{ + overlay_targ_id_t otid; + varpd_impl_t *vip = inst->vri_impl; + + otid.otid_linkid = inst->vri_linkid; + if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_DISASSOCIATE, &otid) != 0) + return (errno); + return (0); +} + +int +libvarpd_overlay_degrade_datalink(varpd_impl_t *vip, datalink_id_t linkid, + const char *msg) +{ + overlay_targ_degrade_t otd; + + otd.otd_linkid = linkid; + (void) strlcpy(otd.otd_buf, msg, OVERLAY_STATUS_BUFLEN); + if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_DEGRADE, &otd) != 0) + return (errno); + return (0); + +} + +int +libvarpd_overlay_degrade(varpd_instance_t *inst, const char *msg) +{ + return (libvarpd_overlay_degrade_datalink(inst->vri_impl, + inst->vri_linkid, msg)); +} + +int +libvarpd_overlay_restore(varpd_instance_t *inst) +{ + overlay_targ_id_t otid; + varpd_impl_t *vip = inst->vri_impl; + + otid.otid_linkid = inst->vri_linkid; + if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_RESTORE, &otid) != 0) + return (errno); + return (0); +} + +int +libvarpd_overlay_packet(varpd_impl_t *vip, const overlay_targ_lookup_t *otl, + void *buf, size_t *buflen) +{ + int ret; + overlay_targ_pkt_t otp; + + otp.otp_linkid = UINT64_MAX; + otp.otp_reqid = otl->otl_reqid; + otp.otp_size = *buflen; + otp.otp_buf = buf; + + do { + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_PKT, &otp); + } while (ret != 0 && errno == EINTR); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_PKT ioctl efault"); + else if (ret != 0) + ret = errno; + + if (ret == 0) + *buflen = otp.otp_size; + + return (ret); +} + +static int +libvarpd_overlay_inject_common(varpd_impl_t *vip, varpd_instance_t *inst, + const overlay_targ_lookup_t *otl, void *buf, size_t buflen, int cmd) +{ + int ret; + overlay_targ_pkt_t otp; + + if (otl == NULL) { + otp.otp_linkid = inst->vri_linkid; + otp.otp_reqid = 0; + } else { + otp.otp_linkid = UINT64_MAX; + otp.otp_reqid = otl->otl_reqid; + } + otp.otp_size = buflen; + otp.otp_buf = buf; + + do { + ret = ioctl(vip->vdi_overlayfd, cmd, &otp); + } while (ret != 0 && errno == EINTR); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("overlay_inject_common ioctl EFAULT"); + else if (ret != 0) + ret = errno; + + return (ret); +} + +int +libvarpd_overlay_inject(varpd_impl_t *vip, const overlay_targ_lookup_t *otl, + void *buf, size_t buflen) +{ + return (libvarpd_overlay_inject_common(vip, NULL, otl, buf, buflen, + OVERLAY_TARG_INJECT)); +} + +int +libvarpd_overlay_instance_inject(varpd_instance_t *inst, void *buf, + size_t buflen) +{ + return (libvarpd_overlay_inject_common(inst->vri_impl, inst, NULL, buf, + buflen, OVERLAY_TARG_INJECT)); +} + +int +libvarpd_overlay_resend(varpd_impl_t *vip, const overlay_targ_lookup_t *otl, + void *buf, size_t buflen) +{ + return (libvarpd_overlay_inject_common(vip, NULL, otl, buf, buflen, + OVERLAY_TARG_RESEND)); +} + +static void +libvarpd_overlay_lookup_reply(varpd_impl_t *vip, + const overlay_targ_lookup_t *otl, overlay_targ_resp_t *otr, int cmd) +{ + int ret; + + otr->otr_reqid = otl->otl_reqid; + do { + ret = ioctl(vip->vdi_overlayfd, cmd, otr); + } while (ret != 0 && errno == EINTR); + + /* + * The only errors that should cause us to end up here are due to + * programmer errors. Aruably the EINAVL case indicates that something + * is a bit off; however, at this time we don't opt to kill varpd. + */ + if (ret != 0 && errno != EINVAL) + libvarpd_panic("receieved bad errno from lookup_reply " + "(cmd %d): %d\n", cmd, errno); +} + +static void +libvarpd_overlay_lookup_handle(varpd_impl_t *vip) +{ + int ret; + varpd_query_t *vqp; + overlay_targ_lookup_t *otl; + overlay_targ_resp_t *otr; + varpd_instance_t *inst; + + vqp = umem_cache_alloc(vip->vdi_qcache, UMEM_DEFAULT); + otl = &vqp->vq_lookup; + otr = &vqp->vq_response; + /* + * abort doesn't really help here that much, maybe we can instead try + * and for a reap or something? + */ + if (vqp == NULL) + libvarpd_panic("failed to allocate memory for lookup " + "handle..., we should not panic()"); + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_LOOKUP, otl); + if (ret != 0 && errno != ETIME && errno != EINTR) + libvarpd_panic("received bad errno from OVERLAY_TARG_LOOKUP: " + "%d", errno); + + if (ret != 0) { + umem_cache_free(vip->vdi_qcache, vqp); + return; + } + + inst = (varpd_instance_t *)libvarpd_instance_lookup( + (varpd_handle_t *)vip, otl->otl_varpdid); + if (inst == NULL) { + libvarpd_overlay_lookup_reply(vip, otl, otr, + OVERLAY_TARG_DROP); + umem_cache_free(vip->vdi_qcache, vqp); + return; + } + vqp->vq_instance = inst; + + inst->vri_plugin->vpp_ops->vpo_lookup(inst->vri_private, + (varpd_query_handle_t *)vqp, otl, &otr->otr_answer); +} + +void +libvarpd_overlay_lookup_run(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + mutex_enter(&vip->vdi_lock); + if (vip->vdi_lthr_quiesce == B_TRUE) { + mutex_exit(&vip->vdi_lock); + return; + } + vip->vdi_lthr_count++; + + for (;;) { + mutex_exit(&vip->vdi_lock); + libvarpd_overlay_lookup_handle(vip); + mutex_enter(&vip->vdi_lock); + if (vip->vdi_lthr_quiesce == B_TRUE) + break; + } + assert(vip->vdi_lthr_count > 0); + vip->vdi_lthr_count--; + (void) cond_signal(&vip->vdi_lthr_cv); + mutex_exit(&vip->vdi_lock); +} + +void +libvarpd_overlay_lookup_quiesce(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + mutex_enter(&vip->vdi_lock); + if (vip->vdi_lthr_count == 0) { + mutex_exit(&vip->vdi_lock); + return; + } + vip->vdi_lthr_quiesce = B_TRUE; + while (vip->vdi_lthr_count > 0) + (void) cond_wait(&vip->vdi_lthr_cv, &vip->vdi_lock); + vip->vdi_lthr_quiesce = B_FALSE; + mutex_exit(&vip->vdi_lock); +} + +int +libvarpd_overlay_iter(varpd_impl_t *vip, libvarpd_overlay_iter_f func, + void *arg) +{ + uint32_t curents = 0, i; + size_t size; + overlay_targ_list_t *otl; + + for (;;) { + size = sizeof (overlay_targ_list_t) + + sizeof (uint32_t) * curents; + otl = umem_alloc(size, UMEM_DEFAULT); + if (otl == NULL) + return (ENOMEM); + + otl->otl_nents = curents; + if (ioctl(vip->vdi_overlayfd, OVERLAY_TARG_LIST, otl) != 0) { + if (errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_LIST ioctl " + "efault"); + umem_free(otl, size); + if (errno == EINTR) + continue; + else + return (errno); + } + + if (otl->otl_nents == curents) + break; + + curents = otl->otl_nents; + umem_free(otl, size); + } + + for (i = 0; i < otl->otl_nents; i++) { + if (func(vip, otl->otl_ents[i], arg) != 0) + break; + } + umem_free(otl, size); + return (0); +} + +int +libvarpd_overlay_cache_flush(varpd_instance_t *inst) +{ + int ret; + overlay_targ_cache_t cache; + varpd_impl_t *vip = inst->vri_impl; + + bzero(&cache, sizeof (overlay_targ_cache_t)); + cache.otc_linkid = inst->vri_linkid; + + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_FLUSH, &cache); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_CACHE_FLUSH ioctl efault"); + else if (ret != 0) + ret = errno; + + return (ret); +} + +int +libvarpd_overlay_cache_delete(varpd_instance_t *inst, const uint8_t *key) +{ + int ret; + overlay_targ_cache_t cache; + varpd_impl_t *vip = inst->vri_impl; + + bzero(&cache, sizeof (overlay_targ_cache_t)); + cache.otc_linkid = inst->vri_linkid; + bcopy(key, cache.otc_entry.otce_mac, ETHERADDRL); + + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_REMOVE, &cache); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_CACHE_REMOVE ioctl efault"); + else if (ret != 0) + ret = errno; + + return (ret); + +} + +int +libvarpd_overlay_cache_get(varpd_instance_t *inst, const uint8_t *key, + varpd_client_cache_entry_t *entry) +{ + int ret; + overlay_targ_cache_t cache; + varpd_impl_t *vip = inst->vri_impl; + + bzero(&cache, sizeof (overlay_targ_cache_t)); + cache.otc_linkid = inst->vri_linkid; + bcopy(key, cache.otc_entry.otce_mac, ETHERADDRL); + + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_GET, &cache); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_CACHE_GET ioctl efault"); + else if (ret != 0) + return (errno); + + bcopy(cache.otc_entry.otce_dest.otp_mac, &entry->vcp_mac, ETHERADDRL); + entry->vcp_flags = cache.otc_entry.otce_flags; + entry->vcp_ip = cache.otc_entry.otce_dest.otp_ip; + entry->vcp_port = cache.otc_entry.otce_dest.otp_port; + + return (0); +} + +int +libvarpd_overlay_cache_set(varpd_instance_t *inst, const uint8_t *key, + const varpd_client_cache_entry_t *entry) +{ + int ret; + overlay_targ_cache_t cache; + varpd_impl_t *vip = inst->vri_impl; + + bzero(&cache, sizeof (overlay_targ_cache_t)); + cache.otc_linkid = inst->vri_linkid; + bcopy(key, cache.otc_entry.otce_mac, ETHERADDRL); + bcopy(&entry->vcp_mac, cache.otc_entry.otce_dest.otp_mac, ETHERADDRL); + cache.otc_entry.otce_flags = entry->vcp_flags; + cache.otc_entry.otce_dest.otp_ip = entry->vcp_ip; + cache.otc_entry.otce_dest.otp_port = entry->vcp_port; + + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_SET, &cache); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_CACHE_SET ioctl efault"); + else if (ret != 0) + return (errno); + + return (0); +} + +int +libvarpd_overlay_cache_walk_fill(varpd_instance_t *inst, uint64_t *markerp, + uint64_t *countp, overlay_targ_cache_entry_t *ents) +{ + int ret; + size_t asize; + overlay_targ_cache_iter_t *iter; + varpd_impl_t *vip = inst->vri_impl; + + if (*countp > 200) + return (E2BIG); + + asize = sizeof (overlay_targ_cache_iter_t) + + *countp * sizeof (overlay_targ_cache_entry_t); + iter = umem_alloc(asize, UMEM_DEFAULT); + if (iter == NULL) + return (ENOMEM); + + iter->otci_linkid = inst->vri_linkid; + iter->otci_marker = *markerp; + iter->otci_count = *countp; + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_ITER, iter); + if (ret != 0 && errno == EFAULT) + libvarpd_panic("OVERLAY_TARG_CACHE_ITER ioctl efault"); + else if (ret != 0) { + ret = errno; + goto out; + } + + *markerp = iter->otci_marker; + *countp = iter->otci_count; + bcopy(iter->otci_ents, ents, + *countp * sizeof (overlay_targ_cache_entry_t)); +out: + umem_free(iter, asize); + return (ret); +} + +void +libvarpd_plugin_query_reply(varpd_query_handle_t *vqh, int action) +{ + varpd_query_t *vqp = (varpd_query_t *)vqh; + + if (vqp == NULL) + libvarpd_panic("unkonwn plugin passed invalid " + "varpd_query_handle_t"); + + if (action == VARPD_LOOKUP_DROP) + libvarpd_overlay_lookup_reply(vqp->vq_instance->vri_impl, + &vqp->vq_lookup, &vqp->vq_response, OVERLAY_TARG_DROP); + else if (action == VARPD_LOOKUP_OK) + libvarpd_overlay_lookup_reply(vqp->vq_instance->vri_impl, + &vqp->vq_lookup, &vqp->vq_response, OVERLAY_TARG_RESPOND); + else + libvarpd_panic("plugin %s passed in an invalid action: %d", + vqp->vq_instance->vri_plugin->vpp_name, action); + + umem_cache_free(vqp->vq_instance->vri_impl->vdi_qcache, vqp); +} + +void +libvarpd_inject_varp(varpd_provider_handle_t *vph, const uint8_t *mac, + const overlay_target_point_t *otp) +{ + int ret; + overlay_targ_cache_t otc; + varpd_instance_t *inst = (varpd_instance_t *)vph; + varpd_impl_t *vip = inst->vri_impl; + + if (otp == NULL) { + (void) libvarpd_overlay_cache_delete(inst, mac); + return; + } + + otc.otc_linkid = inst->vri_linkid; + otc.otc_entry.otce_flags = 0; + bcopy(mac, otc.otc_entry.otce_mac, ETHERADDRL); + bcopy(otp, &otc.otc_entry.otce_dest, sizeof (overlay_target_point_t)); + + ret = ioctl(vip->vdi_overlayfd, OVERLAY_TARG_CACHE_SET, &otc); + if (ret != 0) { + switch (errno) { + case EBADF: + case EFAULT: + case ENOTSUP: + libvarpd_panic("received bad errno from " + "OVERLAY_TARG_CACHE_SET: %d", errno); + default: + break; + } + } +} + +void +libvarpd_fma_degrade(varpd_provider_handle_t *vph, const char *msg) +{ + int ret; + varpd_instance_t *inst = (varpd_instance_t *)vph; + + ret = libvarpd_overlay_degrade(inst, msg); + switch (ret) { + case ENOENT: + case EFAULT: + libvarpd_panic("received bad errno from degrade ioctl: %d", + errno); + default: + break; + } +} + +void +libvarpd_fma_restore(varpd_provider_handle_t *vph) +{ + int ret; + varpd_instance_t *inst = (varpd_instance_t *)vph; + + ret = libvarpd_overlay_restore(inst); + switch (ret) { + case ENOENT: + case EFAULT: + libvarpd_panic("received bad errno from restore ioctl: %d", + errno); + default: + break; + } +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_panic.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_panic.c new file mode 100644 index 0000000000..6728d79d6e --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_panic.c @@ -0,0 +1,53 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + +/* + * No, 'tis not so deep as a well, nor so wide as a church door; but 'tis + * enough,'twill serve. Ask for me tomorrow, and you shall find me a grave man. + * + * This file maintains various routines for handling when we die. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <thread.h> +#include <stdlib.h> + +/* + * Normally these would be static, but if they're static, that throws off lint + * because it thinks we never use them, which is kind of the point, because we + * only read them in the core... + */ +int varpd_panic_errno; +char varpd_panic_buf[1024]; +thread_t varpd_panic_thread; + +void +libvarpd_panic(const char *fmt, ...) +{ + va_list ap; + + /* Always save errno first! */ + varpd_panic_errno = errno; + varpd_panic_thread = thr_self(); + + if (fmt != NULL) { + va_start(ap, fmt); + (void) vsnprintf(varpd_panic_buf, sizeof (varpd_panic_buf), fmt, + ap); + } + abort(); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_persist.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_persist.c new file mode 100644 index 0000000000..27cc802a9c --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_persist.c @@ -0,0 +1,586 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +/* + * varpd persistence backend + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <strings.h> +#include <librename.h> +#include <md5.h> +#include <sys/sysmacros.h> +#include <dirent.h> +#include <sys/mman.h> +#include <umem.h> +#include <sys/debug.h> + +#include <libvarpd_impl.h> + +static uint8_t varpd_persist_magic[4] = { + 'v', + 'a', + 'r', + 'p', +}; + +#define VARPD_PERSIST_MAXWRITE 4096 +#define VARPD_PERSIST_VERSION_ONE 1 +#define VARPD_PERSIST_SUFFIX ".varpd" + +typedef struct varpd_persist_header { + uint8_t vph_magic[4]; + uint32_t vph_version; + uint8_t vph_md5[16]; +} varpd_persist_header_t; + +void +libvarpd_persist_init(varpd_impl_t *vip) +{ + vip->vdi_persistfd = -1; + if (rwlock_init(&vip->vdi_pfdlock, USYNC_THREAD, NULL) != 0) + libvarpd_panic("failed to create rw vdi_pfdlock"); +} + +void +libvarpd_persist_fini(varpd_impl_t *vip) +{ + /* + * Clean up for someone that left something behind. + */ + if (vip->vdi_persistfd != -1) { + if (close(vip->vdi_persistfd) != 0) + libvarpd_panic("failed to close persist fd %d: %d", + vip->vdi_persistfd, errno); + vip->vdi_persistfd = -1; + } + if (rwlock_destroy(&vip->vdi_pfdlock) != 0) + libvarpd_panic("failed to destroy rw vdi_pfdlock"); +} + +int +libvarpd_persist_enable(varpd_handle_t *vhp, const char *rootdir) +{ + int fd; + struct stat st; + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + fd = open(rootdir, O_RDONLY); + if (fd < 0) + return (errno); + + if (fstat(fd, &st) != 0) { + int ret = errno; + if (close(fd) != 0) + libvarpd_panic("failed to close rootdir fd (%s) %d: %d", + rootdir, fd, errno); + return (ret); + } + + if (!S_ISDIR(st.st_mode)) { + if (close(fd) != 0) + libvarpd_panic("failed to close rootdir fd (%s) %d: %d", + rootdir, fd, errno); + return (EINVAL); + } + + + VERIFY0(rw_wrlock(&vip->vdi_pfdlock)); + if (vip->vdi_persistfd != -1) { + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + if (close(fd) != 0) + libvarpd_panic("failed to close rootdir fd (%s) %d: %d", + rootdir, fd, errno); + return (EEXIST); + } + vip->vdi_persistfd = fd; + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + + return (0); +} + +static int +libvarpd_persist_write(int fd, const void *buf, size_t buflen) +{ + ssize_t ret; + off_t off = 0; + + while (buflen > 0) { + ret = write(fd, (void *)((uintptr_t)buf + off), + MIN(buflen, VARPD_PERSIST_MAXWRITE)); + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + return (errno); + + off += ret; + buflen -= ret; + } + + return (0); +} + +static int +libvarpd_persist_nvlist(int dirfd, uint64_t id, nvlist_t *nvl) +{ + int err, fd; + size_t size; + varpd_persist_header_t hdr; + librename_atomic_t *lrap; + char *buf = NULL, *name; + + if ((err = nvlist_pack(nvl, &buf, &size, NV_ENCODE_XDR, 0)) != 0) + return (err); + + if (asprintf(&name, "%llu%s", (unsigned long long)id, ".varpd") == -1) { + err = errno; + free(buf); + return (err); + } + + if ((err = librename_atomic_fdinit(dirfd, name, NULL, 0600, 0, + &lrap)) != 0) { + free(name); + free(buf); + return (err); + } + + fd = librename_atomic_fd(lrap); + + bzero(&hdr, sizeof (varpd_persist_header_t)); + bcopy(varpd_persist_magic, hdr.vph_magic, sizeof (varpd_persist_magic)); + hdr.vph_version = VARPD_PERSIST_VERSION_ONE; + md5_calc(hdr.vph_md5, buf, size); + + if ((err = libvarpd_persist_write(fd, &hdr, + sizeof (varpd_persist_header_t))) != 0) { + librename_atomic_fini(lrap); + free(name); + free(buf); + return (err); + } + + if ((err = libvarpd_persist_write(fd, buf, size)) != 0) { + librename_atomic_fini(lrap); + free(name); + free(buf); + return (err); + } + + do { + err = librename_atomic_commit(lrap); + } while (err == EINTR); + + librename_atomic_fini(lrap); + free(name); + free(buf); + return (err); +} + +int +libvarpd_persist_instance(varpd_impl_t *vip, varpd_instance_t *inst) +{ + int err = 0; + nvlist_t *nvl = NULL, *cvl = NULL; + + VERIFY0(rw_rdlock(&vip->vdi_pfdlock)); + /* Check if persistence exists */ + if (vip->vdi_persistfd == -1) + goto out; + + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto out; + + if ((err = nvlist_alloc(&cvl, NV_UNIQUE_NAME, 0)) != 0) + goto out; + + if ((err = nvlist_add_uint64(nvl, "vri_id", inst->vri_id)) != 0) + goto out; + + if ((err = nvlist_add_uint32(nvl, "vri_linkid", inst->vri_linkid)) != 0) + goto out; + + if ((err = nvlist_add_uint32(nvl, "vri_dest", + (uint32_t)inst->vri_dest)) != 0) + goto out; + if ((err = nvlist_add_uint32(nvl, "vri_mode", + (uint32_t)inst->vri_mode)) != 0) + goto out; + + if ((err = nvlist_add_string(nvl, "vri_plugin", + inst->vri_plugin->vpp_name)) != 0) + goto out; + + err = inst->vri_plugin->vpp_ops->vpo_save(inst->vri_private, cvl); + if (err != 0) + goto out; + + if ((err = nvlist_add_nvlist(nvl, "vri_private", cvl)) != 0) + goto out; + + err = libvarpd_persist_nvlist(vip->vdi_persistfd, inst->vri_id, nvl); +out: + nvlist_free(nvl); + nvlist_free(cvl); + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + return (err); +} + +void +libvarpd_torch_instance(varpd_impl_t *vip, varpd_instance_t *inst) +{ + char buf[32]; + int ret; + + VERIFY0(rw_rdlock(&vip->vdi_pfdlock)); + if (vip->vdi_persistfd == -1) { + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + return; + } + + if (snprintf(buf, sizeof (buf), "%lld.varpd", inst->vri_id) >= 32) + libvarpd_panic("somehow exceeded static value for " + "libvarpd_torch_instance buffer"); + + do { + ret = unlinkat(vip->vdi_persistfd, buf, 0); + } while (ret == -1 && errno == EINTR); + if (ret != 0) { + switch (errno) { + case ENOENT: + break; + default: + libvarpd_panic("failed to unlinkat %d`%s: %s", + vip->vdi_persistfd, buf, strerror(errno)); + } + } + + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); +} + +static int +libvarpd_persist_restore_instance(varpd_impl_t *vip, nvlist_t *nvl) +{ + int err; + nvlist_t *pvl; + uint64_t id, flags, vid; + uint32_t linkid, dest, mode; + char *pluginstr; + varpd_plugin_t *plugin; + overlay_plugin_dest_t adest; + varpd_instance_t *inst, lookup; + + if (nvlist_lookup_uint64(nvl, "vri_id", &id) != 0) + return (EINVAL); + + if (nvlist_lookup_uint32(nvl, "vri_linkid", &linkid) != 0) + return (EINVAL); + + if (nvlist_lookup_uint32(nvl, "vri_dest", &dest) != 0) + return (EINVAL); + + if (nvlist_lookup_uint32(nvl, "vri_mode", &mode) != 0) + return (EINVAL); + + if (nvlist_lookup_string(nvl, "vri_plugin", &pluginstr) != 0) + return (EINVAL); + + if (nvlist_lookup_nvlist(nvl, "vri_private", &pvl) != 0) + return (EINVAL); + + plugin = libvarpd_plugin_lookup(vip, pluginstr); + if (plugin == NULL) + return (EINVAL); + + if (plugin->vpp_mode != mode) + return (EINVAL); + + if (libvarpd_overlay_info(vip, linkid, &adest, &flags, &vid) != 0) + return (EINVAL); + + if (dest != adest) + return (EINVAL); + + inst = umem_alloc(sizeof (varpd_instance_t), UMEM_DEFAULT); + if (inst == NULL) + libvarpd_panic("failed to allocate instance for restore"); + + inst->vri_id = id_alloc_specific(vip->vdi_idspace, id); + if (inst->vri_id != id) { + umem_free(inst, sizeof (varpd_instance_t)); + return (EINVAL); + } + + inst->vri_linkid = linkid; + inst->vri_vnetid = vid; + inst->vri_mode = plugin->vpp_mode; + inst->vri_dest = dest; + inst->vri_plugin = plugin; + inst->vri_impl = vip; + inst->vri_flags = 0; + if (plugin->vpp_ops->vpo_restore(pvl, (varpd_provider_handle_t *)inst, + dest, &inst->vri_private) != 0) { + id_free(vip->vdi_idspace, id); + umem_free(inst, sizeof (varpd_instance_t)); + return (EINVAL); + } + + if (mutex_init(&inst->vri_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL) != 0) + libvarpd_panic("failed to create vri_lock mutex"); + + mutex_enter(&vip->vdi_lock); + lookup.vri_id = inst->vri_id; + if (avl_find(&vip->vdi_instances, &lookup, NULL) != NULL) + libvarpd_panic("found duplicate instance with id %d", + lookup.vri_id); + avl_add(&vip->vdi_instances, inst); + lookup.vri_linkid = inst->vri_linkid; + if (avl_find(&vip->vdi_linstances, &lookup, NULL) != NULL) + libvarpd_panic("found duplicate linstance with id %d", + lookup.vri_linkid); + avl_add(&vip->vdi_linstances, inst); + mutex_exit(&vip->vdi_lock); + + if (plugin->vpp_ops->vpo_start(inst->vri_private) != 0) { + libvarpd_instance_destroy((varpd_instance_handle_t *)inst); + return (EINVAL); + } + + if (flags & OVERLAY_TARG_INFO_F_ACTIVE) + (void) libvarpd_overlay_disassociate(inst); + + if (libvarpd_overlay_associate(inst) != 0) { + libvarpd_instance_destroy((varpd_instance_handle_t *)inst); + return (EINVAL); + } + + if (flags & OVERLAY_TARG_INFO_F_DEGRADED) { + if ((err = libvarpd_overlay_restore(inst)) != 0) { + libvarpd_panic("failed to restore instance %p: %d\n", + inst, err); + } + } + + mutex_enter(&inst->vri_lock); + inst->vri_flags |= VARPD_INSTANCE_F_ACTIVATED; + mutex_exit(&inst->vri_lock); + + return (0); +} + +static int +libvarpd_persist_restore_one(varpd_impl_t *vip, int fd) +{ + int err; + size_t fsize; + struct stat st; + void *buf, *datap; + varpd_persist_header_t *hdr; + uint8_t md5[16]; + nvlist_t *nvl; + + if (fstat(fd, &st) != 0) + return (errno); + + if (st.st_size <= sizeof (varpd_persist_header_t)) + return (EINVAL); + fsize = st.st_size - sizeof (varpd_persist_header_t); + + buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) + return (errno); + + hdr = buf; + if (bcmp(varpd_persist_magic, hdr->vph_magic, + sizeof (varpd_persist_magic)) != 0) { + if (munmap(buf, st.st_size) != 0) + libvarpd_panic("failed to munmap %p: %d", buf, errno); + return (EINVAL); + } + + if (hdr->vph_version != VARPD_PERSIST_VERSION_ONE) { + if (munmap(buf, st.st_size) != 0) + libvarpd_panic("failed to munmap %p: %d", buf, errno); + return (EINVAL); + } + + datap = (void *)((uintptr_t)buf + sizeof (varpd_persist_header_t)); + md5_calc(md5, datap, fsize); + if (bcmp(md5, hdr->vph_md5, sizeof (uint8_t) * 16) != 0) { + if (munmap(buf, st.st_size) != 0) + libvarpd_panic("failed to munmap %p: %d", buf, errno); + return (EINVAL); + } + + err = nvlist_unpack(datap, fsize, &nvl, 0); + if (munmap(buf, st.st_size) != 0) + libvarpd_panic("failed to munmap %p: %d", buf, errno); + + if (err != 0) + return (EINVAL); + + err = libvarpd_persist_restore_instance(vip, nvl); + nvlist_free(nvl); + return (err); +} + +/* ARGSUSED */ +static int +libvarpd_check_degrade_cb(varpd_impl_t *vip, datalink_id_t linkid, void *arg) +{ + varpd_instance_t *inst; + + mutex_enter(&vip->vdi_lock); + for (inst = avl_first(&vip->vdi_instances); inst != NULL; + inst = AVL_NEXT(&vip->vdi_instances, inst)) { + if (inst->vri_linkid == linkid) { + mutex_exit(&vip->vdi_lock); + return (0); + } + } + + mutex_exit(&vip->vdi_lock); + + (void) libvarpd_overlay_degrade_datalink(vip, linkid, + "no varpd instance exists"); + return (0); +} + +static void +libvarpd_check_degrade(varpd_impl_t *vip) +{ + (void) libvarpd_overlay_iter(vip, libvarpd_check_degrade_cb, NULL); +} + +int +libvarpd_persist_restore(varpd_handle_t *vhp) +{ + int dirfd; + int ret = 0; + DIR *dirp = NULL; + struct dirent *dp; + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + VERIFY0(rw_rdlock(&vip->vdi_pfdlock)); + if ((dirfd = dup(vip->vdi_persistfd)) < 0) { + ret = errno; + goto out; + } + + if ((dirp = fdopendir(dirfd)) == NULL) { + ret = errno; + if (close(dirfd) != 0) + libvarpd_panic("failed to close dirfd %d: %d", + dirfd, errno); + goto out; + } + + for (;;) { + int fd; + uint64_t id; + char *eptr; + struct stat st; + + errno = 0; + dp = readdir(dirp); + if (dp == NULL) { + ret = errno; + break; + } + + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + + /* + * Leave files that we don't recognize alone. A valid file has + * the format `%llu.varpd`. + */ + errno = 0; + id = strtoull(dp->d_name, &eptr, 10); + if ((id == 0 && errno == EINVAL) || + (id == ULLONG_MAX && errno == ERANGE)) + continue; + + if (strcmp(eptr, VARPD_PERSIST_SUFFIX) != 0) + continue; + + fd = openat(vip->vdi_persistfd, dp->d_name, O_RDONLY); + if (fd < 0) { + ret = errno; + break; + } + + if (fstat(fd, &st) != 0) { + ret = errno; + break; + } + + if (!S_ISREG(st.st_mode)) { + if (close(fd) != 0) + libvarpd_panic("failed to close fd (%s) %d: " + "%d\n", dp->d_name, fd, errno); + continue; + } + + ret = libvarpd_persist_restore_one(vip, fd); + if (close(fd) != 0) + libvarpd_panic("failed to close fd (%s) %d: " + "%d\n", dp->d_name, fd, errno); + /* + * This is an invalid file. We'll unlink it to save us this + * trouble in the future. + */ + if (ret != 0) { + if (unlinkat(vip->vdi_persistfd, dp->d_name, 0) != 0) { + ret = errno; + break; + } + } + } + + libvarpd_check_degrade(vip); + +out: + if (dirp != NULL) + (void) closedir(dirp); + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + return (ret); +} + +int +libvarpd_persist_disable(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + VERIFY0(rw_wrlock(&vip->vdi_pfdlock)); + if (vip->vdi_persistfd == -1) { + mutex_exit(&vip->vdi_lock); + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + return (ENOENT); + } + if (close(vip->vdi_persistfd) != 0) + libvarpd_panic("failed to close persist fd %d: %d", + vip->vdi_persistfd, errno); + vip->vdi_persistfd = -1; + VERIFY0(rw_unlock(&vip->vdi_pfdlock)); + return (0); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c new file mode 100644 index 0000000000..ac73286fdd --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c @@ -0,0 +1,269 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * varpd plugin management + */ + +#include <libvarpd_impl.h> +#include <errno.h> +#include <umem.h> +#include <assert.h> +#include <strings.h> +#include <dlfcn.h> +#include <link.h> +#include <stdio.h> +#include <bunyan.h> + +static varpd_impl_t *varpd_load_handle; +static const char *varpd_load_path; +static mutex_t varpd_load_lock; +static cond_t varpd_load_cv; + +int +libvarpd_plugin_comparator(const void *lp, const void *rp) +{ + int ret; + const varpd_plugin_t *lpp, *rpp; + + lpp = lp; + rpp = rp; + + ret = strcmp(lpp->vpp_name, rpp->vpp_name); + if (ret > 0) + return (1); + if (ret < 0) + return (-1); + return (0); +} + +varpd_plugin_register_t * +libvarpd_plugin_alloc(uint_t version, int *errp) +{ + int err; + varpd_plugin_register_t *vprp; + + if (errp == NULL) + errp = &err; + + if (version != VARPD_VERSION_ONE) { + (void) bunyan_warn(varpd_load_handle->vdi_bunyan, + "unsupported registration version", + BUNYAN_T_STRING, "module_path", varpd_load_path, + BUNYAN_T_INT32, "module_version", version, + BUNYAN_T_END); + *errp = EINVAL; + return (NULL); + } + + vprp = umem_alloc(sizeof (varpd_plugin_register_t), UMEM_DEFAULT); + if (vprp == NULL) { + (void) bunyan_warn(varpd_load_handle->vdi_bunyan, + "failed to allocate registration handle", + BUNYAN_T_STRING, "module_path", varpd_load_path, + BUNYAN_T_END); + *errp = ENOMEM; + return (NULL); + } + + vprp->vpr_version = VARPD_VERSION_ONE; + + return (vprp); +} + +void +libvarpd_plugin_free(varpd_plugin_register_t *vprp) +{ + umem_free(vprp, sizeof (varpd_plugin_register_t)); +} + +int +libvarpd_plugin_register(varpd_plugin_register_t *vprp) +{ + varpd_plugin_t *vpp; + varpd_plugin_t lookup; + + vpp = umem_alloc(sizeof (varpd_plugin_t), UMEM_DEFAULT); + if (vpp == NULL) { + (void) bunyan_warn(varpd_load_handle->vdi_bunyan, + "failed to allocate memory for the varpd_plugin_t", + BUNYAN_T_STRING, "module_path", varpd_load_path, + BUNYAN_T_END); + return (ENOMEM); + } + + /* Watch out for an evil plugin */ + if (vprp->vpr_version != VARPD_VERSION_ONE) { + (void) bunyan_warn(varpd_load_handle->vdi_bunyan, + "unsupported registration version", + BUNYAN_T_STRING, "module_path", varpd_load_path, + BUNYAN_T_INT32, "module_version", vprp->vpr_version, + BUNYAN_T_END); + return (EINVAL); + } + + mutex_enter(&varpd_load_lock); + if (varpd_load_handle == NULL) + libvarpd_panic("varpd_load_handle was unexpectedly null"); + + mutex_enter(&varpd_load_handle->vdi_lock); + lookup.vpp_name = vprp->vpr_name; + if (avl_find(&varpd_load_handle->vdi_plugins, &lookup, NULL) != NULL) { + (void) bunyan_warn(varpd_load_handle->vdi_bunyan, + "module already exists with requested name", + BUNYAN_T_STRING, "module_path", varpd_load_path, + BUNYAN_T_STRING, "name", vprp->vpr_name, + BUNYAN_T_END); + mutex_exit(&varpd_load_handle->vdi_lock); + mutex_exit(&varpd_load_lock); + umem_free(vpp, sizeof (varpd_plugin_t)); + return (EEXIST); + } + vpp->vpp_name = strdup(vprp->vpr_name); + if (vpp->vpp_name == NULL) { + (void) bunyan_warn(varpd_load_handle->vdi_bunyan, + "failed to allocate memory to duplicate name", + BUNYAN_T_STRING, "module_path", varpd_load_path, + BUNYAN_T_END); + mutex_exit(&varpd_load_handle->vdi_lock); + mutex_exit(&varpd_load_lock); + umem_free(vpp, sizeof (varpd_plugin_t)); + return (ENOMEM); + } + + vpp->vpp_mode = vprp->vpr_mode; + vpp->vpp_ops = vprp->vpr_ops; + if (mutex_init(&vpp->vpp_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL) != 0) + libvarpd_panic("failed to create plugin's vpp_lock"); + vpp->vpp_active = 0; + avl_add(&varpd_load_handle->vdi_plugins, vpp); + mutex_exit(&varpd_load_handle->vdi_lock); + mutex_exit(&varpd_load_lock); + + return (0); +} + +varpd_plugin_t * +libvarpd_plugin_lookup(varpd_impl_t *vip, const char *name) +{ + varpd_plugin_t lookup, *ret; + + lookup.vpp_name = name; + mutex_enter(&vip->vdi_lock); + ret = avl_find(&vip->vdi_plugins, &lookup, NULL); + mutex_exit(&vip->vdi_lock); + + return (ret); +} + +/* ARGSUSED */ +static int +libvarpd_plugin_load_cb(varpd_impl_t *vip, const char *path, void *unused) +{ + void *dlp; + + varpd_load_path = path; + dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW); + if (dlp == NULL) { + (void) bunyan_error(vip->vdi_bunyan, "dlopen failed", + BUNYAN_T_STRING, "module path", path, + BUNYAN_T_END); + } + path = NULL; + + return (0); +} + +int +libvarpd_plugin_load(varpd_handle_t *vph, const char *path) +{ + int ret = 0; + varpd_impl_t *vip = (varpd_impl_t *)vph; + + if (vip == NULL || path == NULL) + return (EINVAL); + mutex_enter(&varpd_load_lock); + while (varpd_load_handle != NULL) + (void) cond_wait(&varpd_load_cv, &varpd_load_lock); + varpd_load_handle = vip; + mutex_exit(&varpd_load_lock); + + ret = libvarpd_dirwalk(vip, path, ".so", libvarpd_plugin_load_cb, NULL); + + mutex_enter(&varpd_load_lock); + varpd_load_handle = NULL; + (void) cond_signal(&varpd_load_cv); + mutex_exit(&varpd_load_lock); + + return (ret); +} + +int +libvarpd_plugin_walk(varpd_handle_t *vph, libvarpd_plugin_walk_f func, + void *arg) +{ + varpd_impl_t *vip = (varpd_impl_t *)vph; + varpd_plugin_t *vpp; + + mutex_enter(&vip->vdi_lock); + for (vpp = avl_first(&vip->vdi_plugins); vpp != NULL; + vpp = AVL_NEXT(&vip->vdi_plugins, vpp)) { + if (func(vph, vpp->vpp_name, arg) != 0) { + mutex_exit(&vip->vdi_lock); + return (1); + } + } + mutex_exit(&vip->vdi_lock); + return (0); +} + +void +libvarpd_plugin_init(void) +{ + if (mutex_init(&varpd_load_lock, USYNC_THREAD | LOCK_RECURSIVE | + LOCK_ERRORCHECK, NULL) != 0) + libvarpd_panic("failed to create varpd_load_lock"); + + if (cond_init(&varpd_load_cv, USYNC_THREAD, NULL) != 0) + libvarpd_panic("failed to create varpd_load_cv"); + + varpd_load_handle = NULL; +} + +void +libvarpd_plugin_fini(void) +{ + assert(varpd_load_handle == NULL); + if (mutex_destroy(&varpd_load_lock) != 0) + libvarpd_panic("failed to destroy varpd_load_lock"); + if (cond_destroy(&varpd_load_cv) != 0) + libvarpd_panic("failed to destroy varpd_load_cv"); +} + +void +libvarpd_plugin_prefork(void) +{ + mutex_enter(&varpd_load_lock); + while (varpd_load_handle != NULL) + (void) cond_wait(&varpd_load_cv, &varpd_load_lock); +} + +void +libvarpd_plugin_postfork(void) +{ + (void) cond_signal(&varpd_load_cv); + mutex_exit(&varpd_load_lock); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_prop.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_prop.c new file mode 100644 index 0000000000..f3a1492408 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_prop.c @@ -0,0 +1,300 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * varpd property management + */ + +#include <libvarpd_impl.h> +#include <errno.h> +#include <strings.h> +#include <sys/mac.h> +#include <umem.h> + +typedef struct varpd_prop_info { + varpd_impl_t *vprop_vip; + varpd_instance_t *vprop_instance; + uint_t vprop_type; + uint_t vprop_prot; + uint32_t vprop_defsize; + uint32_t vprop_psize; + char vprop_name[LIBVARPD_PROP_NAMELEN]; + uint8_t vprop_default[LIBVARPD_PROP_SIZEMAX]; + uint8_t vprop_poss[LIBVARPD_PROP_SIZEMAX]; +} varpd_prop_info_t; + +/* Internal Properties */ +static int varpd_nintprops = 1; +static const char *varpd_intprops[] = { + "search" +}; + +static int +libvarpd_prop_get_search(varpd_prop_info_t *infop, void *buf, uint32_t *sizep) +{ + varpd_plugin_t *vpp = infop->vprop_instance->vri_plugin; + size_t nlen; + + nlen = strlen(vpp->vpp_name) + 1; + if (nlen > *sizep) + return (EOVERFLOW); + *sizep = nlen; + (void) strlcpy(buf, vpp->vpp_name, *sizep); + return (0); +} + +void +libvarpd_prop_set_name(varpd_prop_handle_t *phdl, const char *name) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + (void) strlcpy(infop->vprop_name, name, OVERLAY_PROP_NAMELEN); +} + +void +libvarpd_prop_set_prot(varpd_prop_handle_t *phdl, overlay_prop_prot_t perm) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + infop->vprop_prot = perm; +} + +void +libvarpd_prop_set_type(varpd_prop_handle_t *phdl, overlay_prop_type_t type) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + infop->vprop_type = type; +} + +int +libvarpd_prop_set_default(varpd_prop_handle_t *phdl, void *buf, ssize_t len) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + + if (len > LIBVARPD_PROP_SIZEMAX) + return (E2BIG); + + if (len < 0) + return (EOVERFLOW); + + bcopy(buf, infop->vprop_default, len); + infop->vprop_defsize = len; + return (0); +} + +void +libvarpd_prop_set_nodefault(varpd_prop_handle_t *phdl) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + + infop->vprop_default[0] = '\0'; + infop->vprop_defsize = 0; +} + +void +libvarpd_prop_set_range_uint32(varpd_prop_handle_t *phdl, uint32_t min, + uint32_t max) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + mac_propval_range_t *rangep = (mac_propval_range_t *)infop->vprop_poss; + + if (rangep->mpr_count != 0 && rangep->mpr_type != MAC_PROPVAL_UINT32) + return; + + if (infop->vprop_psize + sizeof (mac_propval_uint32_range_t) > + sizeof (infop->vprop_poss)) + return; + + infop->vprop_psize += sizeof (mac_propval_uint32_range_t); + rangep->mpr_count++; + rangep->mpr_type = MAC_PROPVAL_UINT32; + rangep->u.mpr_uint32[rangep->mpr_count-1].mpur_min = min; + rangep->u.mpr_uint32[rangep->mpr_count-1].mpur_max = max; +} + +void +libvarpd_prop_set_range_str(varpd_prop_handle_t *phdl, const char *str) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + size_t len = strlen(str) + 1; /* Account for a null terminator */ + mac_propval_range_t *rangep = (mac_propval_range_t *)infop->vprop_poss; + mac_propval_str_range_t *pstr = &rangep->u.mpr_str; + + if (rangep->mpr_count != 0 && rangep->mpr_type != MAC_PROPVAL_STR) + return; + + if (infop->vprop_psize + len > sizeof (infop->vprop_poss)) + return; + + rangep->mpr_count++; + rangep->mpr_type = MAC_PROPVAL_STR; + (void) strlcpy((char *)&pstr->mpur_data[pstr->mpur_nextbyte], str, + sizeof (infop->vprop_poss) - infop->vprop_psize); + pstr->mpur_nextbyte += len; + infop->vprop_psize += len; +} + +int +libvarpd_prop_handle_alloc(varpd_handle_t *vph, varpd_instance_handle_t *inst, + varpd_prop_handle_t **phdlp) +{ + varpd_prop_info_t *infop; + + infop = umem_alloc(sizeof (varpd_prop_info_t), UMEM_DEFAULT); + if (infop == NULL) + return (ENOMEM); + + bzero(infop, sizeof (varpd_prop_info_t)); + infop->vprop_vip = (varpd_impl_t *)vph; + infop->vprop_instance = (varpd_instance_t *)inst; + + *phdlp = (varpd_prop_handle_t *)infop; + return (0); +} + +void +libvarpd_prop_handle_free(varpd_prop_handle_t *phdl) +{ + umem_free(phdl, sizeof (varpd_prop_info_t)); +} + +int +libvarpd_prop_nprops(varpd_instance_handle_t *ihdl, uint_t *np) +{ + int ret; + varpd_instance_t *instp = (varpd_instance_t *)ihdl; + + ret = instp->vri_plugin->vpp_ops->vpo_nprops(instp->vri_private, np); + if (ret != 0) + return (ret); + *np += varpd_nintprops; + return (0); +} + +static int +libvarpd_prop_info_fill_int_cb(varpd_handle_t *handle, const char *name, + void *arg) +{ + varpd_prop_handle_t *vph = arg; + libvarpd_prop_set_range_str(vph, name); + return (0); +} + +static int +libvarpd_prop_info_fill_int(varpd_prop_handle_t *vph, uint_t propid) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)vph; + if (propid >= varpd_nintprops) + abort(); + libvarpd_prop_set_name(vph, varpd_intprops[0]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_READ); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_STRING); + libvarpd_prop_set_nodefault(vph); + (void) libvarpd_plugin_walk( + (varpd_handle_t *)infop->vprop_instance->vri_impl, + libvarpd_prop_info_fill_int_cb, vph); + return (0); +} + +int +libvarpd_prop_info_fill(varpd_prop_handle_t *phdl, uint_t propid) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + varpd_instance_t *instp = infop->vprop_instance; + mac_propval_range_t *rangep = (mac_propval_range_t *)infop->vprop_poss; + + infop->vprop_psize = sizeof (mac_propval_range_t); + + bzero(rangep, sizeof (mac_propval_range_t)); + if (propid < varpd_nintprops) { + return (libvarpd_prop_info_fill_int(phdl, propid)); + } else { + varpd_plugin_t *vpp = instp->vri_plugin; + return (vpp->vpp_ops->vpo_propinfo(instp->vri_private, + propid - varpd_nintprops, phdl)); + } +} + +int +libvarpd_prop_info(varpd_prop_handle_t *phdl, const char **namep, + uint_t *typep, uint_t *protp, const void **defp, uint32_t *sizep, + const mac_propval_range_t **possp) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + if (namep != NULL) + *namep = infop->vprop_name; + if (typep != NULL) + *typep = infop->vprop_type; + if (protp != NULL) + *protp = infop->vprop_prot; + if (defp != NULL) + *defp = infop->vprop_default; + if (sizep != NULL) + *sizep = infop->vprop_psize; + if (possp != NULL) + *possp = (mac_propval_range_t *)infop->vprop_poss; + return (0); +} + +int +libvarpd_prop_get(varpd_prop_handle_t *phdl, void *buf, uint32_t *sizep) +{ + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + varpd_instance_t *instp = infop->vprop_instance; + + if (infop->vprop_name[0] == '\0') + return (EINVAL); + + if (strcmp(varpd_intprops[0], infop->vprop_name) == 0) { + /* search property */ + return (libvarpd_prop_get_search(infop, buf, sizep)); + } + + return (instp->vri_plugin->vpp_ops->vpo_getprop(instp->vri_private, + infop->vprop_name, buf, sizep)); +} + +int +libvarpd_prop_set(varpd_prop_handle_t *phdl, const void *buf, uint32_t size) +{ + int i; + varpd_prop_info_t *infop = (varpd_prop_info_t *)phdl; + varpd_instance_t *instp = infop->vprop_instance; + + if (infop->vprop_name[0] == '\0') + return (EINVAL); + + for (i = 0; i < varpd_nintprops; i++) { + if (strcmp(infop->vprop_name, varpd_intprops[i]) == 0) { + return (EPERM); + } + } + + return (instp->vri_plugin->vpp_ops->vpo_setprop(instp->vri_private, + infop->vprop_name, buf, size)); +} + +void +libvarpd_prop_door_convert(const varpd_prop_handle_t *phdl, + varpd_client_propinfo_arg_t *vcfap) +{ + const varpd_prop_info_t *infop = (const varpd_prop_info_t *)phdl; + + vcfap->vcfa_type = infop->vprop_type; + vcfap->vcfa_prot = infop->vprop_prot; + vcfap->vcfa_defsize = infop->vprop_defsize; + vcfap->vcfa_psize = infop->vprop_psize; + bcopy(infop->vprop_name, vcfap->vcfa_name, LIBVARPD_PROP_NAMELEN); + bcopy(infop->vprop_default, vcfap->vcfa_default, LIBVARPD_PROP_SIZEMAX); + bcopy(infop->vprop_poss, vcfap->vcfa_poss, LIBVARPD_PROP_SIZEMAX); +} diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h b/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h new file mode 100644 index 0000000000..64fa99d308 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h @@ -0,0 +1,419 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_PROVIDER_H +#define _LIBVARPD_PROVIDER_H + +/* + * varpd provider interface for lookup modules + * + * This header file defines all the structures and functions that a given lookup + * module needs to implement and perform its purpose. At this time, all of these + * interfaces are considered private to illumos and therefore are subject to + * change. At some point we will move to more broadly stabilize these interfaces + * and commit to them. Until such time, expect breakage for out of gate + * consumers. + * + * A plugin is a dynamic shared object that is placed inside of varpd's default + * module. + * + * The shared object must define an initializer, such as with #pragma init. This + * function will be run with the module is dlopened by libvarpd. In that init + * function, the function must allocate a varpd_plugin_register by calling + * libvarpd_plugin_alloc() and specifying VARPD_CURRENT_VERSION. If that + * succeeds, then it should proceed to fill out the registration and then call, + * libvarpd_plugin_register() with it. Regardless of whether it succeeds or + * fails, it should call libvarpd_plugin_free(). In the case of failure, there + * is not much that the module should do, other than log some message to the + * standard bunyan logger that exists. + * + * Once libvarpd_plugin_register() returns, the module should assume that any + * of the operations it defined in the operation vector may be called and + * therefore it is recommended that any other required initialization should be + * performed at that time. + * + * At this time, once a plugin is loaded, it will not be unloaded. Therefore, + * there is no corresponding requirement to unregister, though that may come in + * a future version. + * + * ----------------------------- + * Plugin Types and Destinations + * ----------------------------- + * + * There are two different kinds of plugins in this world, there are point to + * point plugins and there are dynamic plugins. The key difference is in how + * packets are routed through the system. In a point to point plugin, a single + * destination is used when the instance is started. In dynamic plugins, + * destinations are looked up as they are required and an instance of a plugin + * is required to provide that. + * + * These point to point plugins define a type of OVERLAY_TARGET_POINT and the + * dynamic plugins instead define a type of OVERLAY_TARGET_DYNAMIC. + * + * Encapsulation plugins have multiple types of destinations. They may require + * an Ethernet address (OVERLAY_PLUGIN_D_ETHERNET), IP address + * (OVERLAY_PLUGIN_D_IP), and a port (OVERLAY_PLUGIN_D_PORT). For example, + * consider vxlan, it requires an IP and a port; while a hypothetical nvgre, + * would only require an IP. + * + * A plugin is allowed to describe which of these fields that it supports and + * given which encapsulation plugin it is paired with, it can support a varying + * degree of properties. For example, consider the example of the direct plugin. + * It has a notion of a destination port and a destination IP. If it is paired + * with a plugin that only requires an IP, then it wouldn't need to show a + * property that's related to a destination port. + * + * ------------------ + * Plugin Definitions + * ------------------ + * + * A plugin is required to fill in both an operations vector and a series of + * additional metadata that it passed in to libvarpd_plugin_register(). The + * following lists all of the routines and their purposes. The full signatures + * are available in the body of the header file. + * + * varpd_plugin_create_f + * + * Create a new instance of a plugin. Each instance refers to a different + * overlay device and thus a different overlay identifier. Each instance + * has its own property space and is unique. This function gives the chance + * for the plugin to create and provide any private data that it will + * require. + * + * In addition, the plugin is given the type of destination that is + * required and it is its job to determine whether or not it supports it. + * + * varpd_plugin_destory_f + * + * This is the opposite of varpd_plugin_create_f. It is called to allow the + * plugin to reclaim any resources with the private argument that it passed + * out as part of the destroy function. + * + * varpd_plugin_start_f + * + * This routine is called to indicate that an instance should be started. + * This is a plugin's chance to verify that it has all of its required + * properties set and to take care of any action that needs to be handled + * to begin the plugin. After this point it will be legal to have the + * varpd_plugin_default_f, varpd_plugin_lookup_f, varpd_plugin_arp_f and + * varpd_plugin_dhcp_f endpoints called. + * + * varpd_plugin_stop_f + * + * This routine is called to indicate that an instance is stopping, it is + * the opposite of varpd_plugin_start_f. This is a chance to clean up + * resources that are a side effect of having started the instance. + * + * varpd_plugin_default_f + * + * This routine is defined by plugins of type OVERLAY_TARGET_POINT. It is + * used to answer the question of where should all traffic for this + * instance be destined. Plugins of type OVERLAY_TARGET_DYNAMIC should + * leave this entry set to NULL. + * + * On success, the default routine should return VARPD_LOOKUP_OK. On + * failure, it should return the macro VARPD_LOOKUP_DROP. + * + * varpd_plugin_lookup_f + * + * This routine must be defined by plugins of type OVERLAY_TARGET_DYNAMIC. + * It is used to lookup the destination for a given request. Each request + * comes in with its own MAC address this allows a plugin to direct it to + * any remote location. + * + * This is designed as an asynchronous API. Once a lookup is completed it + * should call libvarpd_plugin_query_reply() and pass as the second + * argument either VARPD_LOOKUP_OK to indicate that it went alright or it + * should reply VARPD_LOOKUP_DROP to indicate that the packet should be + * dropped. + * + * In addition, there are several utility routines that can take care of + * various kinds of traffic automatically. For example, if an ARP, NDP, or + * DHCP packet comes in, there are utilities such as + * libvarpd_plugin_proxy_arp(), libvarpd_plugin_proxy_ndp() and + * libvarpd_plugin_proxy_dhcp(), which allows the system to do the heavy + * lifting of validating the packet once it finds that it matches certain + * properties. + * + * varpd_plugin_arp_f + * + * This is an optional entry for plugins of type OVERLAY_TARGET_DYNAMIC. + * This is called after a plugin calls libvarpd_plugin_proxy_arp() and is + * used to ask the plugin to perform an ARP or NDP query. The type of query + * is passed in in the third argument, the only valid value for which will + * be VARPD_QTYPE_ETHERNET, to indicate we're doing an Ethernet lookup. + * + * The layer three IP address that is being looked up will be included in + * the struct sockaddr. The sockaddr(3SOCKET)'s sa_family will be set to + * indicate the type, eg. AF_INET or AF_INET6 and that will indicate the + * kind of sockaddr that will be used. For more information see + * sockaddr(3SOCKET). The implementation ensures that enough space for the + * link layer address will exist. + * + * This is an asynchronous lookup. Once the answer has been written, a + * plugin should call libvarpd_plugin_arp_reply and if it was successful, + * VARPD_LOOKUP_OK should be passed in and if it failed, VARPD_LOOKUP_DROP + * should be passed in instead. + * + * varpd_plugin_dhcp_f + * + * This is an optional entry for plugins of type OVERLAY_TARGET_DYNAMIC. + * This is called after a plugin calls the libvarpd_plugin_proxy_dhcp() and + * is used to ask the plugin to determine where is the DHCP server that + * this packet should actually be sent to. What is happening here is that + * rather than broadcast the initial DHCP request, we instead unicast it to + * a specified DHCP server that this operation vector indicates. + * + * The plugin is given a type, the same as the ARP plugin which indicates + * the kind of link layer address, the only valid type is + * VARPD_QTYPE_ETHERNET, other types should be rejected. Then, like the arp + * entry point, the dhcp entry point should determine the link layer + * address of the DHCP server and write that out in the appropriate memory + * and call libvarpd_plugin_dhcp_reply() when done. Similar to the arp + * entry point, it should use VARPD_LOOKUP_OK to indicate that it was + * filled in and VARPD_LOOKUP_DROP to indicate that it was not. + * + * varpd_plugin_nprops_f + * + * This is used by a plugin to indicate the number of properties that + * should exist for this instance. Recall from the section that Plugin + * types and Destinations, that the number of entries here may vary. As + * such, the plugin should return the number that is appropriate for the + * instance. + * + * This number will be used to obtain information about a property via the + * propinfo functions. However, the getprop and setprop interfaces will + * always use names to indicate the property it is getting and setting. + * This difference is structured this way to deal with property discovery + * and to make the getprop and setprop interfaces slightly easier for other + * parts of the broader varpd/dladm infrastructure. + * + * varpd_plugin_propinfo_f + * + * This interface is used to get information about a property, the property + * that information is being requested for is being passed in via the + * second argument. Here, callers should set properties such as the name, + * the protection, whether or not the property is required, set any default + * value, if it exist, and if relevant, set the valid range of values. + * + * varpd_plugin_getprop_f + * + * This is used to get the value of a property, if it is set. The passed in + * length indicates the length of the buffer that is used for updating + * properties. If it is not of sufficient size, the function should return + * an error and not update the buffer. Otherwise, it should update the size + * pointer with the valid size. + * + * varpd_plugin_setprop_f + * + * This is used to set the value of a property. An endpoint should validate + * that the property is valid before updating it. In addition, it should + * update its state as appropriate. + * + * varpd_plugin_save_f + * + * This is used to serialize the state of a given instance of a plugin such + * that if varpd crashes, it can be recovered. The plugin should write all + * state into the nvlist that it is passed in, it may use any keys and + * values that it wants. The only consumer of that nvlist will be the + * plugin itself when the restore endpoint is called. + * + * varpd_plugin_restore_f + * + * This is called by the server to restore an instance that used to exist, + * but was lost due to a crash. This is a combination of calling create and + * setting properties. The plugin should restore any private state that it + * can find recorded from the nvlist. The only items in the nvlist will be + * those that were written out during a previous call to + * varpd_plugin_save_f. + * + * + * Once all of these interfaces are implemented, the plugin should define the + * following members in the varpd_plugin_register_t. + * + * vpr_version + * + * This indicates the version of the plugin. Plugins should set this to the + * macro VARPD_CURRENT_VERSION. + * + * vpr_mode + * + * This indicates the mode of the plugin. The plugin's mode should be one + * of OVERLAY_TARGET_POINT and OVERLAY_TARGET_DYNAMIC. For more discussion + * of these types and the differences, see the section on Plugin Types and + * Destinations. + * + * vpr_name + * + * This is the name of the plugin. This is how users will refer to it in + * the context of running dladm(1M) commands. Note, this name must be + * unique across the different plugins, as it will cause others with the + * same name not to be registered. + * + * vpr_ops + * + * This is the operations vector as described above. Importantly, the + * member vpo_callbacks must be set to zero, this is being used for future + * expansion of the structure. + * + * + * -------------------------------------------------- + * Downcalls, Upcalls, and Synchronization Guarantees + * -------------------------------------------------- + * + * Every instance of a plugin is independent. Calls into a plugin may be made + * for different instances in parallel. Any necessary locking is left to the + * plugin module. Within an instance, various calls may come in parallel. + * + * The primary guarantees are that none of the varpd_plugin_save_f, + * varpd_plugin_lookup_f, varpd_default_f, varpd_plugin_arp_f, and + * varpd_plugin_dhcp_f will be called until after a call to varpd_plugin_start_f + * has been called. Similarly, they will not be called after a call to + * vardp_plugin_stop_f. + * + * The functions documented in this header may be called back into from any + * context, including from the operation vectors. + */ + +#include <bunyan.h> +#include <libvarpd.h> +#include <libnvpair.h> +#include <sys/socket.h> +#include <sys/overlay_target.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define VARPD_VERSION_ONE 1 +#define VARPD_CURRENT_VERSION VARPD_VERSION_ONE + +typedef struct __varpd_provier_handle varpd_provider_handle_t; +typedef struct __varpd_query_handle varpd_query_handle_t; +typedef struct __varpd_arp_handle varpd_arp_handle_t; +typedef struct __varpd_dhcp_handle varpd_dhcp_handle_t; + +typedef int (*varpd_plugin_create_f)(varpd_provider_handle_t *, void **, + overlay_plugin_dest_t); +typedef int (*varpd_plugin_start_f)(void *); +typedef void (*varpd_plugin_stop_f)(void *); +typedef void (*varpd_plugin_destroy_f)(void *); + +#define VARPD_LOOKUP_OK (0) +#define VARPD_LOOKUP_DROP (-1) +typedef int (*varpd_plugin_default_f)(void *, overlay_target_point_t *); +typedef void (*varpd_plugin_lookup_f)(void *, varpd_query_handle_t *, + const overlay_targ_lookup_t *, overlay_target_point_t *); + +#define VARPD_QTYPE_ETHERNET 0x0 +typedef void (*varpd_plugin_arp_f)(void *, varpd_arp_handle_t *, int, + const struct sockaddr *, uint8_t *); +typedef void (*varpd_plugin_dhcp_f)(void *, varpd_dhcp_handle_t *, int, + const overlay_targ_lookup_t *, uint8_t *); + +typedef int (*varpd_plugin_nprops_f)(void *, uint_t *); +typedef int (*varpd_plugin_propinfo_f)(void *, const uint_t, + varpd_prop_handle_t *); +typedef int (*varpd_plugin_getprop_f)(void *, const char *, void *, uint32_t *); +typedef int (*varpd_plugin_setprop_f)(void *, const char *, const void *, + const uint32_t); + +typedef int (*varpd_plugin_save_f)(void *, nvlist_t *); +typedef int (*varpd_plugin_restore_f)(nvlist_t *, varpd_provider_handle_t *, + overlay_plugin_dest_t, void **); + +typedef struct varpd_plugin_ops { + uint_t vpo_callbacks; + varpd_plugin_create_f vpo_create; + varpd_plugin_start_f vpo_start; + varpd_plugin_stop_f vpo_stop; + varpd_plugin_destroy_f vpo_destroy; + varpd_plugin_default_f vpo_default; + varpd_plugin_lookup_f vpo_lookup; + varpd_plugin_nprops_f vpo_nprops; + varpd_plugin_propinfo_f vpo_propinfo; + varpd_plugin_getprop_f vpo_getprop; + varpd_plugin_setprop_f vpo_setprop; + varpd_plugin_save_f vpo_save; + varpd_plugin_restore_f vpo_restore; + varpd_plugin_arp_f vpo_arp; + varpd_plugin_dhcp_f vpo_dhcp; +} varpd_plugin_ops_t; + +typedef struct varpd_plugin_register { + uint_t vpr_version; + uint_t vpr_mode; + const char *vpr_name; + const varpd_plugin_ops_t *vpr_ops; +} varpd_plugin_register_t; + +extern varpd_plugin_register_t *libvarpd_plugin_alloc(uint_t, int *); +extern void libvarpd_plugin_free(varpd_plugin_register_t *); +extern int libvarpd_plugin_register(varpd_plugin_register_t *); + +/* + * Blowing up and logging + */ +extern void libvarpd_panic(const char *, ...) __NORETURN; +extern const bunyan_logger_t *libvarpd_plugin_bunyan(varpd_provider_handle_t *); + +/* + * Misc. Information APIs + */ +extern uint64_t libvarpd_plugin_vnetid(varpd_provider_handle_t *); + +/* + * Lookup Replying query and proxying + */ +extern void libvarpd_plugin_query_reply(varpd_query_handle_t *, int); + +extern void libvarpd_plugin_proxy_arp(varpd_provider_handle_t *, + varpd_query_handle_t *, const overlay_targ_lookup_t *); +extern void libvarpd_plugin_proxy_ndp(varpd_provider_handle_t *, + varpd_query_handle_t *, const overlay_targ_lookup_t *); +extern void libvarpd_plugin_arp_reply(varpd_arp_handle_t *, int); + +extern void libvarpd_plugin_proxy_dhcp(varpd_provider_handle_t *, + varpd_query_handle_t *, const overlay_targ_lookup_t *); +extern void libvarpd_plugin_dhcp_reply(varpd_dhcp_handle_t *, int); + + +/* + * Property information callbacks + */ +extern void libvarpd_prop_set_name(varpd_prop_handle_t *, const char *); +extern void libvarpd_prop_set_prot(varpd_prop_handle_t *, overlay_prop_prot_t); +extern void libvarpd_prop_set_type(varpd_prop_handle_t *, overlay_prop_type_t); +extern int libvarpd_prop_set_default(varpd_prop_handle_t *, void *, ssize_t); +extern void libvarpd_prop_set_nodefault(varpd_prop_handle_t *); +extern void libvarpd_prop_set_range_uint32(varpd_prop_handle_t *, uint32_t, + uint32_t); +extern void libvarpd_prop_set_range_str(varpd_prop_handle_t *, const char *); + +/* + * Various injecting and invalidation routines + */ +extern void libvarpd_inject_varp(varpd_provider_handle_t *, const uint8_t *, + const overlay_target_point_t *); +extern void libvarpd_inject_arp(varpd_provider_handle_t *, const uint16_t, + const uint8_t *, const struct in_addr *, const uint8_t *); +extern void libvarpd_fma_degrade(varpd_provider_handle_t *, const char *); +extern void libvarpd_fma_restore(varpd_provider_handle_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_PROVIDER_H */ diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_util.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_util.c new file mode 100644 index 0000000000..e21fed2126 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_util.c @@ -0,0 +1,97 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#include <libvarpd_impl.h> +#include <assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +const char * +libvarpd_isaext(void) +{ +#if defined(__sparc) +#if defined(__sparcv9) + return ("64"); +#else /* __sparcv9 */ + return (""); +#endif /* __sparvc9 */ +#elif defined(__amd64) + return ("64"); +#elif defined(__i386) + return (""); +#else +#error "unkonwn ISA" +#endif +} + +int +libvarpd_dirwalk(varpd_impl_t *vip, const char *path, const char *suffix, + libvarpd_dirwalk_f func, void *arg) +{ + int ret; + size_t slen; + char *dirpath, *filepath; + DIR *dirp; + struct dirent *dp; + assert(vip != NULL && path != NULL); + + if (asprintf(&dirpath, "%s/%s", path, libvarpd_isaext()) == -1) + return (errno); + + if ((dirp = opendir(dirpath)) == NULL) { + ret = errno; + return (ret); + } + + slen = strlen(suffix); + for (;;) { + size_t len; + + errno = 0; + dp = readdir(dirp); + if (dp == NULL) { + ret = errno; + break; + } + + len = strlen(dp->d_name); + if (len <= slen) + continue; + + if (strcmp(suffix, dp->d_name + (len - slen)) != 0) + continue; + + if (asprintf(&filepath, "%s/%s", dirpath, dp->d_name) == -1) { + ret = errno; + break; + } + + if (func(vip, filepath, arg) != 0) { + free(filepath); + ret = 0; + break; + } + + free(filepath); + } + + (void) closedir(dirp); + free(dirpath); + return (ret); +} diff --git a/usr/src/lib/varpd/libvarpd/common/llib-lvarpd b/usr/src/lib/varpd/libvarpd/common/llib-lvarpd new file mode 100644 index 0000000000..85150d3463 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/llib-lvarpd @@ -0,0 +1,19 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <libvarpd.h> diff --git a/usr/src/lib/varpd/libvarpd/common/mapfile-plugin b/usr/src/lib/varpd/libvarpd/common/mapfile-plugin new file mode 100644 index 0000000000..8cef7f669f --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/mapfile-plugin @@ -0,0 +1,57 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_SCOPE { + global: + libvarpd_fma_degrade { FLAGS = EXTERN }; + libvarpd_inject_arp { FLAGS = EXTERN }; + libvarpd_inject_ndp { FLAGS = EXTERN }; + libvarpd_inject_varp { FLAGS = EXTERN }; + libvarpd_fma_restore { FLAGS = EXTERN }; + libvarpd_panic { FLAGS = EXTERN }; + libvarpd_plugin_alloc { FLAGS = EXTERN }; + libvarpd_plugin_arp_reply { FLAGS = EXTERN }; + libvarpd_plugin_dhcp_reply { FLAGS = EXTERN }; + libvarpd_plugin_free { FLAGS = EXTERN }; + libvarpd_plugin_proxy_arp { FLAGS = EXTERN }; + libvarpd_plugin_proxy_dhcp { FLAGS = EXTERN }; + libvarpd_plugin_proxy_ndp { FLAGS = EXTERN }; + libvarpd_plugin_query_reply { FLAGS = EXTERN }; + libvarpd_plugin_register { FLAGS = EXTERN }; + libvarpd_plugin_vnetid { FLAGS = EXTERN }; + libvarpd_prop_set_name { FLAGS = EXTERN }; + libvarpd_prop_set_prot { FLAGS = EXTERN }; + libvarpd_prop_set_type { FLAGS = EXTERN }; + libvarpd_prop_set_default { FLAGS = EXTERN }; + libvarpd_prop_set_nodefault { FLAGS = EXTERN }; + libvarpd_prop_set_range_uint32 { FLAGS = EXTERN }; + libvarpd_prop_set_rangestr { FLAGS = EXTERN }; +}; diff --git a/usr/src/lib/varpd/libvarpd/common/mapfile-vers b/usr/src/lib/varpd/libvarpd/common/mapfile-vers new file mode 100644 index 0000000000..7aa930cb54 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/mapfile-vers @@ -0,0 +1,113 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate { + global: + libvarpd_c_create; + libvarpd_c_destroy; + libvarpd_c_instance_activate; + libvarpd_c_instance_create; + libvarpd_c_instance_destroy; + libvarpd_c_prop_nprops; + libvarpd_c_prop_handle_alloc; + libvarpd_c_prop_handle_free; + libvarpd_c_prop_info_fill; + libvarpd_c_prop_info_fill_by_name; + libvarpd_c_prop_info; + libvarpd_c_prop_get; + libvarpd_c_prop_set; + + libvarpd_c_instance_lookup; + libvarpd_c_instance_target_mode; + libvarpd_c_instance_cache_flush; + libvarpd_c_instance_cache_delete; + libvarpd_c_instance_cache_get; + libvarpd_c_instance_cache_set; + libvarpd_c_instance_cache_walk; + + libvarpd_create; + libvarpd_destroy; + + libvarpd_door_server_create; + libvarpd_door_server_destroy; + + libvarpd_fma_degrade; + libvarpd_fma_restore; + + libvarpd_inject_varp; + libvarpd_inject_arp; + + libvarpd_instance_activate; + libvarpd_instance_create; + libvarpd_instance_destroy; + libvarpd_instance_lookup; + libvarpd_instance_id; + + libvarpd_panic; + + libvarpd_persist_disable; + libvarpd_persist_enable; + libvarpd_persist_restore; + + libvarpd_plugin_alloc; + libvarpd_plugin_load; + libvarpd_plugin_free; + libvarpd_plugin_arp_reply; + libvarpd_plugin_dhcp_reply; + libvarpd_plugin_query_reply; + libvarpd_plugin_proxy_arp; + libvarpd_plugin_proxy_dhcp; + libvarpd_plugin_proxy_ndp; + libvarpd_plugin_register; + libvarpd_plugin_walk; + libvarpd_plugin_vnetid; + + libvarpd_prop_set_default; + libvarpd_prop_set_nodefault; + libvarpd_prop_set_name; + libvarpd_prop_set_prot; + libvarpd_prop_set_range_uint32; + libvarpd_prop_set_range_str; + libvarpd_prop_set_type; + + libvarpd_prop_handle_alloc; + libvarpd_prop_handle_free; + libvarpd_prop_nprops; + libvarpd_prop_info_fill; + libvarpd_prop_info; + libvarpd_prop_get; + libvarpd_prop_set; + + libvarpd_overlay_lookup_quiesce; + libvarpd_overlay_lookup_run; + local: + *; +}; diff --git a/usr/src/lib/varpd/libvarpd/i386/Makefile b/usr/src/lib/varpd/libvarpd/i386/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/libvarpd/sparc/Makefile b/usr/src/lib/varpd/libvarpd/sparc/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/libvarpd/sparcv9/Makefile b/usr/src/lib/varpd/libvarpd/sparcv9/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/svp/Makefile b/usr/src/lib/varpd/svp/Makefile new file mode 100644 index 0000000000..b4dbf8f017 --- /dev/null +++ b/usr/src/lib/varpd/svp/Makefile @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +include ../../Makefile.lib + +HDRS = libvarpd_svp_prot.h # For snoop +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +install: install_h +install_h: $(ROOTHDRS) + +check: + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ diff --git a/usr/src/lib/varpd/svp/Makefile.com b/usr/src/lib/varpd/svp/Makefile.com new file mode 100644 index 0000000000..d5e6d73144 --- /dev/null +++ b/usr/src/lib/varpd/svp/Makefile.com @@ -0,0 +1,54 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +LIBRARY = libvarpd_svp.a +VERS = .1 +OBJECTS = libvarpd_svp.o \ + libvarpd_svp_conn.o \ + libvarpd_svp_crc.o \ + libvarpd_svp_host.o \ + libvarpd_svp_loop.o \ + libvarpd_svp_remote.o \ + libvarpd_svp_shootdown.o \ + libvarpd_svp_timer.o + +include ../../../Makefile.lib +include ../../Makefile.plugin + +LIBS = $(DYNLIB) + +# +# Yes, this isn't a command, but libcmdutils does have the list(9F) +# functions and better to use that then compile list.o yet again +# ourselves... probably. +# +LDLIBS += -lc -lumem -lnvpair -lsocket -lavl \ + -lcmdutils -lidspace -lbunyan +CPPFLAGS += -I../common + +LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN +LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN +SRCDIR = ../common + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../Makefile.targ diff --git a/usr/src/lib/varpd/svp/amd64/Makefile b/usr/src/lib/varpd/svp/amd64/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/svp/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp.c b/usr/src/lib/varpd/svp/common/libvarpd_svp.c new file mode 100644 index 0000000000..58828065a1 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp.c @@ -0,0 +1,1138 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + +/* + * This plugin implements the SDC VXLAN Protocol (SVP). + * + * This plugin is designed to work with a broader distributed system that + * mainains a database of mappings and provides a means of looking up data and + * provides a stream of updates. While it is named after VXLAN, there isn't + * anything specific to VXLAN baked into the protocol at this time, other than + * that it requires both an IP address and a port; however, if there's a good + * reason to support others here, we can modify that. + * + * ----------- + * Terminology + * ----------- + * + * Throughout this module we refer to a few different kinds of addresses: + * + * VL3 + * + * A VL3 address, or virtual layer 3, refers to the layer three addreses + * that are used by entities on an overlay network. As far as we're + * concerned that means that this is the IP address of an interface on an + * overlay network. + * + * VL2 + * + * A VL2 address, or a virtual layer 2, referes to the link-layer addresses + * that are used by entities on an overlay network. As far as we're + * concerned that means that this is the MAC addresses of an interface on + * an overlay network. + * + * UL3 + * + * A UL3, or underlay layer 3, refers to the layer three (IP) address on + * the underlay network. + * + * The svp plugin provides lookups from VL3->VL2, eg. the equivalent of an ARP + * or NDP query, and then also provides VL2->UL3 lookups. + * + * ------------------- + * Protocol Operations + * ------------------- + * + * The svp protocol is defined in lib/varpd/svp/common/libvarpd_svp_prot.h. It + * defines the basic TCP protocol that we use to communicate to hosts. At this + * time, it is not quite 100% implemented in both this plug-in and our primary + * server, sdc-portolan (see https://github.com/joyent/sdc-portolan). + * + * At this time, we don't quite support everything that we need to. Including + * the SVP_R_BULK_REQ and SVP_R_SHOOTDOWN. + * + * --------------------------------- + * General Design and Considerations + * --------------------------------- + * + * Every instance of the svp plugin requires the hostname and port of a server + * to contact. Though, we have co-opted the port 1296 (the year of the oldest + * extant portolan) as our default port. + * + * Each of the different instance of the plugins has a corresponding remote + * backend. The remote backend represents the tuple of the [ host, port ]. + * Different instances that share the same host and port tuple will use the same + * backend. + * + * The backend is actually in charge of performing lookups, resolving and + * updating the set of remote hosts based on the DNS resolution we've been + * provided, and taking care of things like shootdowns. + * + * The whole plugin itself maintains an event loop and a number of threads to + * service that event loop. On top of that event loop, we have a simple timer + * backend that ticks at one second intervals and performs various callbacks, + * such as idle query timers, DNS resolution, connection backoff, etc. Each of + * the remote hosts that we obtain is wrapped up in an svp_conn_t, which manages + * the connection state, reconnecting, etc. + * + * All in all, the general way that this all looks like is: + * + * +----------------------------+ + * | Plugin Instance | + * | svp_t | + * | | + * | varpd_provider_handle_t * -+-> varpd handle + * | uint64_t ----+-> varpd ID + * | char * ----+-> remote host + * | uint16_t ----+-> remote port + * | svp_remote_t * ---+------+-> remote backend + * +---------------------+------+ + * | + * v + * +----------------------+ +----------------+ + * | Remote backend |------------------>| Remove Backend |---> ... + * | svp_remote_t | | svp_remote_t | + * | | +----------------+ + * | svp_remote_state_t --+-> state flags + * | svp_degrade_state_t -+-> degraded reason + * | struct addrinfo * --+-> resolved hosts + * | uint_t ---+-> active hosts + * | uint_t ---+-> DNS generation + * | uint_t ---+-> Reference count + * | uint_t ---+-> active conns + * | uint_t ---+-> degraded conns + * | list_t ---+---+-> connection list + * +------------------+---+ + * | + * +------------------------------+-----------------+ + * | | | + * v v v + * +-------------------+ +---------------- + * | SVP Connection | | SVP connection | ... + * | svp_conn_t | | svp_conn_t | + * | | +----------------+ + * | svp_event_t ----+-> event loop handle + * | svp_timer_t ----+-> backoff timer + * | svp_timer_t ----+-> query timer + * | int ----+-> socket fd + * | uint_t ----+-> generation + * | uint_t ----+-> current backoff + * | svp_conn_flags_t -+-> connection flags + * | svp_conn_state_t -+-> connection state + * | svp_conn_error_t -+-> connection error + * | int ---+-> last errrno + * | hrtime_t ---+-> activity timestamp + * | svp_conn_out_t ---+-> outgoing data state + * | svp_conn_in_t ---+-> incoming data state + * | list_t ---+--+-> active queries + * +----------------+--+ + * | + * +----------------------------------+-----------------+ + * | | | + * v v v + * +--------------------+ +-------------+ + * | SVP Query | | SVP Query | ... + * | svp_query_t | | svp_query_t | + * | | +-------------+ + * | svp_query_f ---+-> callback function + * | void * ---+-> callback arg + * | svp_query_state_t -+-> state flags + * | svp_req_t ---+-> svp prot. header + * | svp_query_data_t --+-> read data + * | svp_query_data_t --+-> write data + * | svp_status_t ---+-> request status + * +--------------------+ + * + * The svp_t is the instance that we assoicate with varpd. The instance itself + * maintains properties and then when it's started associates with an + * svp_remote_t, which is the remote backend. The remote backend itself, + * maintains the DNS state and spins up and downs connections based on the + * results from DNS. By default, we query DNS every 30 seconds. For more on the + * connection life cycle, see the next section. + * + * By default, each connection maintains its own back off timer and list of + * queries it's servicing. Only one request is generally outstanding at a time + * and requests are round robined across the various connections. + * + * The query itself represents the svp request that's going on and keep track of + * its state and is a place for data that's read and written to as part of the + * request. + * + * Connections maintain a query timer such that if we have not received data on + * a socket for a certain amount of time, we kill that socket and begin a + * reconnection cycle with backoff. + * + * ------------------------ + * Connection State Machine + * ------------------------ + * + * We have a connection pool that's built upon DNS records. DNS describes the + * membership of the set of remote peers that make up our pool and we maintain + * one connection to each of them. In addition, we maintain an exponential + * backoff for each peer and will attempt to reconect immediately before backing + * off. The following are the valid states that a connection can be in: + * + * SVP_CS_ERROR An OS error has occurred on this connection, + * such as failure to create a socket or associate + * the socket with an event port. We also + * transition all connections to this state before + * we destroy them. + * + * SVP_CS_INITIAL This is the initial state of a connection, all + * that should exist is an unbound socket. + * + * SVP_CS_CONNECTING A call to connect has been made and we are + * polling for it to complete. + * + * SVP_CS_BACKOFF A connect attempt has failed and we are + * currently backing off, waiting to try again. + * + * SVP_CS_ACTIVE We have successfully connected to the remote + * system. + * + * SVP_CS_WINDDOWN This connection is going to valhalla. In other + * words, a previously active connection is no + * longer valid in DNS, so we should curb our use + * of it, and reap it as soon as we have other + * active connections. + * + * The following diagram attempts to describe our state transition scheme, and + * when we transition from one state to the next. + * + * | + * * New remote IP from DNS resolution, + * | not currently active in the system. + * | + * v Socket Error, + * +----------------+ still in DNS + * +----------------<---| SVP_CS_INITIAL |<----------------------*-----+ + * | +----------------+ | + * | System | | + * | Connection . . . . . success * Successful | + * | failed . | connect() | + * | +----*---------+ | +-----------*--+ | + * | | | | | | | + * | V ^ v ^ V ^ + * | +----------------+ +-------------------+ +---------------+ + * +<-| SVP_CS_BACKOFF | | SVP_CS_CONNECTING | | SVP_CS_ACTIVE | + * | +----------------+ +-------------------+ +---------------+ + * | V ^ V V V + * | Backoff wait * | | | * Removed + * v interval +--------------+ +-----------------<-----+ | from DNS + * | finished | | + * | V | + * | | V + * | | +-----------------+ + * +----------------+----------<-----+-------<----| SVP_CS_WINDDOWN | + * | +-----------------+ + * * . . . Fatal system, not + * | socket error or + * V quiesced after + * +--------------+ removal from DNS + * | SVP_CS_ERROR | + * +--------------+ + * | + * * . . . Removed from DNS + * v + * +------------+ + * | Connection | + * | Destroyed | + * +------------+ + * + * -------------------------- + * Connection Event Injection + * -------------------------- + * + * For each connection that exists in the system, we have a timer in place that + * is in charge of performing timeout activity. It fires once every thirty + * seconds or so for a given connection and checks to ensure that we have had + * activity for the most recent query on the connection. If not, it terminates + * the connection. This is important as if we have sent all our data and are + * waiting for the remote end to reply, without enabling something like TCP + * keep-alive, we will not be notified that anything that has happened to the + * remote connection, for example a panic. In addition, this also protects + * against a server that is up, but a portolan that is not making forward + * progress. + * + * When a timeout occurs, we first try to disassociate any active events, which + * by definition must exist. Once that's done, we inject a port source user + * event. Now, there is a small gotcha. Let's assume for a moment that we have a + * pathological portolan. That means that it knows to inject activity right at + * the time out window. That means, that the event may be disassociated before + * we could get to it. If that's the case, we must _not_ inject the user event + * and instead, we'll let the pending event take care of it. We know that the + * pending event hasn't hit the main part of the loop yet, otherwise, it would + * have released the lock protecting our state and associated the event. + * + * ------------ + * Notes on DNS + * ------------ + * + * Unfortunately, doing host name resolution in a way that allows us to leverage + * the system's resolvers and the system's caching, require us to make blocking + * calls in libc via getaddrinfo(3SOCKET). If we can't reach a given server, + * that will tie up a thread for quite some time. To work around that fact, + * we're going to create a fixed number of threads and we'll use them to service + * our DNS requests. While this isn't ideal, until we have a sane means of + * integrating a DNS resolution into an event loop with say portfs, it's not + * going to be a fun day no matter what we do. + * + * ------ + * Timers + * ------ + * + * We maintain a single timer based on CLOCK_REALTIME. It's designed to fire + * every second. While we'd rather use CLOCK_HIGHRES just to alleviate ourselves + * from timer drift; however, as zones may not actually have CLOCK_HIGHRES + * access, we don't want them to end up in there. The timer itself is just a + * simple avl tree sorted by expiration time, which is stored as a tick in the + * future, a tick is just one second. + * + * ---------- + * Shootdowns + * ---------- + * + * As part of the protocol, we need to be able to handle shootdowns that inform + * us some of the information in the system is out of date. This information + * needs to be processed promptly; however, the information is hopefully going + * to be relatively infrequent relative to the normal flow of information. + * + * The shoot down information needs to be done on a per-backend basis. The + * general design is that we'll have a single query for this which can fire on a + * 5-10s period, we randmoize the latter part to give us a bit more load + * spreading. If we complete because there's no work to do, then we wait the + * normal period. If we complete, but there's still work to do, we'll go again + * after a second. + * + * A shootdown has a few different parts. We first receive a list of items to + * shootdown. After performing all of those, we need to acknowledge them. When + * that's been done successfully, we can move onto the next part. From a + * protocol perspective, we make a SVP_R_LOG_REQ, we get a reply, and then after + * processing them, send an SVP_R_LOG_RM. Only once that's been acked do we + * continue. + * + * However, one of the challenges that we have is that these invalidations are + * just that, an invalidation. For a virtual layer two request, that's fine, + * because the kernel supports that. However, for virtual layer three + * invalidations, we have a bit more work to do. These protocols, ARP and NDP, + * don't really support a notion of just an invalidation, instead you have to + * inject the new data in a gratuitous fashion. + * + * To that end, what we instead do is when we receive a VL3 invalidation, we + * turn that info a VL3 request. We hold the general request as outstanding + * until we receive all of the callbacks for the VL3 invalidations, at which + * point we go through and do the log removal request. + */ + +#include <umem.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <libnvpair.h> +#include <strings.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> + +#include <libvarpd_provider.h> +#include "libvarpd_svp.h" + +bunyan_logger_t *svp_bunyan; +static int svp_defport = 1296; +static int svp_defuport = 1339; +static umem_cache_t *svp_lookup_cache; + +typedef enum svp_lookup_type { + SVP_L_UNKNOWN = 0x0, + SVP_L_VL2 = 0x1, + SVP_L_VL3 = 0x2 +} svp_lookup_type_t; + +typedef struct svp_lookup { + int svl_type; + union { + struct svl_lookup_vl2 { + varpd_query_handle_t *svl_handle; + overlay_target_point_t *svl_point; + } svl_vl2; + struct svl_lookup_vl3 { + varpd_arp_handle_t *svl_vah; + uint8_t *svl_out; + } svl_vl3; + } svl_u; + svp_query_t svl_query; +} svp_lookup_t; + +static const char *varpd_svp_props[] = { + "svp/host", + "svp/port", + "svp/underlay_ip", + "svp/underlay_port" +}; + +static const uint8_t svp_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +int +svp_comparator(const void *l, const void *r) +{ + const svp_t *ls = l; + const svp_t *rs = r; + + if (ls->svp_vid > rs->svp_vid) + return (1); + if (ls->svp_vid < rs->svp_vid) + return (-1); + return (0); +} + +static void +svp_vl2_lookup_cb(svp_t *svp, svp_status_t status, const struct in6_addr *uip, + const uint16_t uport, void *arg) +{ + svp_lookup_t *svl = arg; + overlay_target_point_t *otp; + + assert(svp != NULL); + assert(arg != NULL); + + if (status != SVP_S_OK) { + libvarpd_plugin_query_reply(svl->svl_u.svl_vl2.svl_handle, + VARPD_LOOKUP_DROP); + umem_cache_free(svp_lookup_cache, svl); + return; + } + + otp = svl->svl_u.svl_vl2.svl_point; + bcopy(uip, &otp->otp_ip, sizeof (struct in6_addr)); + otp->otp_port = uport; + libvarpd_plugin_query_reply(svl->svl_u.svl_vl2.svl_handle, + VARPD_LOOKUP_OK); + umem_cache_free(svp_lookup_cache, svl); +} + +static void +svp_vl3_lookup_cb(svp_t *svp, svp_status_t status, const uint8_t *vl2mac, + const struct in6_addr *uip, const uint16_t uport, void *arg) +{ + overlay_target_point_t point; + svp_lookup_t *svl = arg; + + assert(svp != NULL); + assert(svl != NULL); + + if (status != SVP_S_OK) { + libvarpd_plugin_arp_reply(svl->svl_u.svl_vl3.svl_vah, + VARPD_LOOKUP_DROP); + umem_cache_free(svp_lookup_cache, svl); + return; + } + + /* Inject the L2 mapping before the L3 */ + bcopy(uip, &point.otp_ip, sizeof (struct in6_addr)); + point.otp_port = uport; + libvarpd_inject_varp(svp->svp_hdl, vl2mac, &point); + + bcopy(vl2mac, svl->svl_u.svl_vl3.svl_out, ETHERADDRL); + libvarpd_plugin_arp_reply(svl->svl_u.svl_vl3.svl_vah, + VARPD_LOOKUP_OK); + umem_cache_free(svp_lookup_cache, svl); +} + +static void +svp_vl2_invalidate_cb(svp_t *svp, const uint8_t *vl2mac) +{ + libvarpd_inject_varp(svp->svp_hdl, vl2mac, NULL); +} + +static void +svp_vl3_inject_cb(svp_t *svp, const uint16_t vlan, const struct in6_addr *vl3ip, + const uint8_t *vl2mac, const uint8_t *targmac) +{ + struct in_addr v4; + + /* + * At the moment we don't support any IPv6 related log entries, this + * will change soon as we develop a bit more of the IPv6 related + * infrastructure so we can properly test the injection. + */ + if (IN6_IS_ADDR_V4MAPPED(vl3ip) == 0) { + return; + } else { + IN6_V4MAPPED_TO_INADDR(vl3ip, &v4); + if (targmac == NULL) + targmac = svp_bcast; + libvarpd_inject_arp(svp->svp_hdl, vlan, vl2mac, &v4, targmac); + } +} + +/* ARGSUSED */ +static void +svp_shootdown_cb(svp_t *svp, const uint8_t *vl2mac, const struct in6_addr *uip, + const uint16_t uport) +{ + /* + * We should probably do a conditional invlaidation here. + */ + libvarpd_inject_varp(svp->svp_hdl, vl2mac, NULL); +} + +static svp_cb_t svp_defops = { + svp_vl2_lookup_cb, + svp_vl3_lookup_cb, + svp_vl2_invalidate_cb, + svp_vl3_inject_cb, + svp_shootdown_cb +}; + +static boolean_t +varpd_svp_valid_dest(overlay_plugin_dest_t dest) +{ + if (dest != (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT)) + return (B_FALSE); + + return (B_TRUE); +} + +static int +varpd_svp_create(varpd_provider_handle_t *hdl, void **outp, + overlay_plugin_dest_t dest) +{ + int ret; + svp_t *svp; + + if (varpd_svp_valid_dest(dest) == B_FALSE) + return (ENOTSUP); + + svp = umem_zalloc(sizeof (svp_t), UMEM_DEFAULT); + if (svp == NULL) + return (ENOMEM); + + if ((ret = mutex_init(&svp->svp_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL)) != 0) { + umem_free(svp, sizeof (svp_t)); + return (ret); + } + + svp->svp_port = svp_defport; + svp->svp_uport = svp_defuport; + svp->svp_cb = svp_defops; + svp->svp_hdl = hdl; + svp->svp_vid = libvarpd_plugin_vnetid(svp->svp_hdl); + *outp = svp; + return (0); +} + +static int +varpd_svp_start(void *arg) +{ + int ret; + svp_remote_t *srp; + svp_t *svp = arg; + + mutex_enter(&svp->svp_lock); + if (svp->svp_host == NULL || svp->svp_port == 0 || + svp->svp_huip == B_FALSE || svp->svp_uport == 0) { + mutex_exit(&svp->svp_lock); + return (EAGAIN); + } + mutex_exit(&svp->svp_lock); + + if ((ret = svp_remote_find(svp->svp_host, svp->svp_port, &svp->svp_uip, + &srp)) != 0) + return (ret); + + if ((ret = svp_remote_attach(srp, svp)) != 0) { + svp_remote_release(srp); + return (ret); + } + + return (0); +} + +static void +varpd_svp_stop(void *arg) +{ + svp_t *svp = arg; + + svp_remote_detach(svp); +} + +static void +varpd_svp_destroy(void *arg) +{ + svp_t *svp = arg; + + if (svp->svp_host != NULL) + umem_free(svp->svp_host, strlen(svp->svp_host) + 1); + + if (mutex_destroy(&svp->svp_lock) != 0) + libvarpd_panic("failed to destroy svp_t`svp_lock"); + + umem_free(svp, sizeof (svp_t)); +} + +static void +varpd_svp_lookup(void *arg, varpd_query_handle_t *vqh, + const overlay_targ_lookup_t *otl, overlay_target_point_t *otp) +{ + svp_lookup_t *slp; + svp_t *svp = arg; + + /* + * Check if this is something that we need to proxy, eg. arp or ndp. + */ + if (otl->otl_sap == ETHERTYPE_ARP) { + libvarpd_plugin_proxy_arp(svp->svp_hdl, vqh, otl); + return; + } + + if (otl->otl_dstaddr[0] == 0x33 && + otl->otl_dstaddr[1] == 0x33) { + if (otl->otl_sap == ETHERTYPE_IPV6) { + libvarpd_plugin_proxy_ndp(svp->svp_hdl, vqh, otl); + } else { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + } + return; + } + + /* + * Watch out for various multicast and broadcast addresses. We've + * already taken care of the IPv6 range above. Now we just need to + * handle broadcast and if the multicast bit is set, lowest bit of the + * first octet of the MAC, then we drop it now. + */ + if (bcmp(otl->otl_dstaddr, svp_bcast, ETHERADDRL) == 0 || + (otl->otl_dstaddr[0] & 0x01) == 0x01) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + return; + } + + /* + * If we have a failure to allocate memory for this, that's not good. + * However, telling the kernel to just drop this packet is much better + * than the alternative at this moment. At least we'll try again and we + * may have something more available to us in a little bit. + */ + slp = umem_cache_alloc(svp_lookup_cache, UMEM_DEFAULT); + if (slp == NULL) { + libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP); + return; + } + + slp->svl_type = SVP_L_VL2; + slp->svl_u.svl_vl2.svl_handle = vqh; + slp->svl_u.svl_vl2.svl_point = otp; + + svp_remote_vl2_lookup(svp, &slp->svl_query, otl->otl_dstaddr, slp); +} + +/* ARGSUSED */ +static int +varpd_svp_nprops(void *arg, uint_t *nprops) +{ + *nprops = sizeof (varpd_svp_props) / sizeof (char *); + return (0); +} + +/* ARGSUSED */ +static int +varpd_svp_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph) +{ + switch (propid) { + case 0: + /* svp/host */ + libvarpd_prop_set_name(vph, varpd_svp_props[0]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_STRING); + libvarpd_prop_set_nodefault(vph); + break; + case 1: + /* svp/port */ + libvarpd_prop_set_name(vph, varpd_svp_props[1]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_UINT); + (void) libvarpd_prop_set_default(vph, &svp_defport, + sizeof (svp_defport)); + libvarpd_prop_set_range_uint32(vph, 1, UINT16_MAX); + break; + case 2: + /* svp/underlay_ip */ + libvarpd_prop_set_name(vph, varpd_svp_props[2]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_IP); + libvarpd_prop_set_nodefault(vph); + break; + case 3: + /* svp/underlay_port */ + libvarpd_prop_set_name(vph, varpd_svp_props[3]); + libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW); + libvarpd_prop_set_type(vph, OVERLAY_PROP_T_UINT); + (void) libvarpd_prop_set_default(vph, &svp_defuport, + sizeof (svp_defuport)); + libvarpd_prop_set_range_uint32(vph, 1, UINT16_MAX); + break; + default: + return (EINVAL); + } + return (0); +} + +static int +varpd_svp_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep) +{ + svp_t *svp = arg; + + /* svp/host */ + if (strcmp(pname, varpd_svp_props[0]) == 0) { + size_t len; + + mutex_enter(&svp->svp_lock); + if (svp->svp_host == NULL) { + *sizep = 0; + } else { + len = strlen(svp->svp_host) + 1; + if (*sizep < len) { + mutex_exit(&svp->svp_lock); + return (EOVERFLOW); + } + *sizep = len; + (void) strlcpy(buf, svp->svp_host, *sizep); + } + mutex_exit(&svp->svp_lock); + return (0); + } + + /* svp/port */ + if (strcmp(pname, varpd_svp_props[1]) == 0) { + uint64_t val; + + if (*sizep < sizeof (uint64_t)) + return (EOVERFLOW); + + mutex_enter(&svp->svp_lock); + if (svp->svp_port == 0) { + *sizep = 0; + } else { + val = svp->svp_port; + bcopy(&val, buf, sizeof (uint64_t)); + *sizep = sizeof (uint64_t); + } + + mutex_exit(&svp->svp_lock); + return (0); + } + + /* svp/underlay_ip */ + if (strcmp(pname, varpd_svp_props[2]) == 0) { + if (*sizep > sizeof (struct in6_addr)) + return (EOVERFLOW); + mutex_enter(&svp->svp_lock); + if (svp->svp_huip == B_FALSE) { + *sizep = 0; + } else { + bcopy(&svp->svp_uip, buf, sizeof (struct in6_addr)); + *sizep = sizeof (struct in6_addr); + } + return (0); + } + + /* svp/underlay_port */ + if (strcmp(pname, varpd_svp_props[3]) == 0) { + uint64_t val; + + if (*sizep < sizeof (uint64_t)) + return (EOVERFLOW); + + mutex_enter(&svp->svp_lock); + if (svp->svp_uport == 0) { + *sizep = 0; + } else { + val = svp->svp_uport; + bcopy(&val, buf, sizeof (uint64_t)); + *sizep = sizeof (uint64_t); + } + + mutex_exit(&svp->svp_lock); + return (0); + } + + return (EINVAL); +} + +static int +varpd_svp_setprop(void *arg, const char *pname, const void *buf, + const uint32_t size) +{ + svp_t *svp = arg; + + /* svp/host */ + if (strcmp(pname, varpd_svp_props[0]) == 0) { + char *dup; + dup = umem_alloc(size, UMEM_DEFAULT); + (void) strlcpy(dup, buf, size); + if (dup == NULL) + return (ENOMEM); + mutex_enter(&svp->svp_lock); + if (svp->svp_host != NULL) + umem_free(svp->svp_host, strlen(svp->svp_host) + 1); + svp->svp_host = dup; + mutex_exit(&svp->svp_lock); + return (0); + } + + /* svp/port */ + if (strcmp(pname, varpd_svp_props[1]) == 0) { + const uint64_t *valp = buf; + if (size < sizeof (uint64_t)) + return (EOVERFLOW); + + if (*valp == 0 || *valp > UINT16_MAX) + return (EINVAL); + + mutex_enter(&svp->svp_lock); + svp->svp_port = (uint16_t)*valp; + mutex_exit(&svp->svp_lock); + return (0); + } + + /* svp/underlay_ip */ + if (strcmp(pname, varpd_svp_props[2]) == 0) { + const struct in6_addr *ipv6 = buf; + + if (size < sizeof (struct in6_addr)) + return (EOVERFLOW); + + if (IN6_IS_ADDR_V4COMPAT(ipv6)) + return (EINVAL); + + if (IN6_IS_ADDR_MULTICAST(ipv6)) + return (EINVAL); + + if (IN6_IS_ADDR_6TO4(ipv6)) + return (EINVAL); + + if (IN6_IS_ADDR_V4MAPPED(ipv6)) { + ipaddr_t v4; + IN6_V4MAPPED_TO_IPADDR(ipv6, v4); + if (IN_MULTICAST(v4)) + return (EINVAL); + } + + mutex_enter(&svp->svp_lock); + bcopy(buf, &svp->svp_uip, sizeof (struct in6_addr)); + svp->svp_huip = B_TRUE; + mutex_exit(&svp->svp_lock); + return (0); + } + + /* svp/underlay_port */ + if (strcmp(pname, varpd_svp_props[3]) == 0) { + const uint64_t *valp = buf; + if (size < sizeof (uint64_t)) + return (EOVERFLOW); + + if (*valp == 0 || *valp > UINT16_MAX) + return (EINVAL); + + mutex_enter(&svp->svp_lock); + svp->svp_uport = (uint16_t)*valp; + mutex_exit(&svp->svp_lock); + + return (0); + } + + return (EINVAL); +} + +static int +varpd_svp_save(void *arg, nvlist_t *nvp) +{ + int ret; + svp_t *svp = arg; + + mutex_enter(&svp->svp_lock); + if (svp->svp_host != NULL) { + if ((ret = nvlist_add_string(nvp, varpd_svp_props[0], + svp->svp_host)) != 0) { + mutex_exit(&svp->svp_lock); + return (ret); + } + } + + if (svp->svp_port != 0) { + if ((ret = nvlist_add_uint16(nvp, varpd_svp_props[1], + svp->svp_port)) != 0) { + mutex_exit(&svp->svp_lock); + return (ret); + } + } + + if (svp->svp_huip == B_TRUE) { + char buf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, &svp->svp_uip, buf, sizeof (buf)) == + NULL) + libvarpd_panic("unexpected inet_ntop failure: %d", + errno); + + if ((ret = nvlist_add_string(nvp, varpd_svp_props[2], + buf)) != 0) { + mutex_exit(&svp->svp_lock); + return (ret); + } + } + + if (svp->svp_uport != 0) { + if ((ret = nvlist_add_uint16(nvp, varpd_svp_props[3], + svp->svp_uport)) != 0) { + mutex_exit(&svp->svp_lock); + return (ret); + } + } + + mutex_exit(&svp->svp_lock); + return (0); +} + +static int +varpd_svp_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl, + overlay_plugin_dest_t dest, void **outp) +{ + int ret; + svp_t *svp; + char *ipstr, *hstr; + + if (varpd_svp_valid_dest(dest) == B_FALSE) + return (ENOTSUP); + + if ((ret = varpd_svp_create(hdl, (void **)&svp, dest)) != 0) + return (ret); + + if ((ret = nvlist_lookup_string(nvp, varpd_svp_props[0], + &hstr)) != 0) { + if (ret != ENOENT) { + varpd_svp_destroy(svp); + return (ret); + } + svp->svp_host = NULL; + } else { + size_t blen = strlen(hstr) + 1; + svp->svp_host = umem_alloc(blen, UMEM_DEFAULT); + (void) strlcpy(svp->svp_host, hstr, blen); + } + + if ((ret = nvlist_lookup_uint16(nvp, varpd_svp_props[1], + &svp->svp_port)) != 0) { + if (ret != ENOENT) { + varpd_svp_destroy(svp); + return (ret); + } + svp->svp_port = 0; + } + + if ((ret = nvlist_lookup_string(nvp, varpd_svp_props[2], + &ipstr)) != 0) { + if (ret != ENOENT) { + varpd_svp_destroy(svp); + return (ret); + } + svp->svp_huip = B_FALSE; + } else { + ret = inet_pton(AF_INET6, ipstr, &svp->svp_uip); + if (ret == -1) { + assert(errno == EAFNOSUPPORT); + libvarpd_panic("unexpected inet_pton failure: %d", + errno); + } + + if (ret == 0) { + varpd_svp_destroy(svp); + return (EINVAL); + } + svp->svp_huip = B_TRUE; + } + + if ((ret = nvlist_lookup_uint16(nvp, varpd_svp_props[3], + &svp->svp_uport)) != 0) { + if (ret != ENOENT) { + varpd_svp_destroy(svp); + return (ret); + } + svp->svp_uport = 0; + } + + svp->svp_hdl = hdl; + *outp = svp; + return (0); +} + +static void +varpd_svp_arp(void *arg, varpd_arp_handle_t *vah, int type, + const struct sockaddr *sock, uint8_t *out) +{ + svp_t *svp = arg; + svp_lookup_t *svl; + + if (type != VARPD_QTYPE_ETHERNET) { + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); + return; + } + + svl = umem_cache_alloc(svp_lookup_cache, UMEM_DEFAULT); + if (svl == NULL) { + libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP); + return; + } + + svl->svl_type = SVP_L_VL3; + svl->svl_u.svl_vl3.svl_vah = vah; + svl->svl_u.svl_vl3.svl_out = out; + svp_remote_vl3_lookup(svp, &svl->svl_query, sock, svl); +} + +static const varpd_plugin_ops_t varpd_svp_ops = { + 0, + varpd_svp_create, + varpd_svp_start, + varpd_svp_stop, + varpd_svp_destroy, + NULL, + varpd_svp_lookup, + varpd_svp_nprops, + varpd_svp_propinfo, + varpd_svp_getprop, + varpd_svp_setprop, + varpd_svp_save, + varpd_svp_restore, + varpd_svp_arp, + NULL +}; + +static int +svp_bunyan_init(void) +{ + int ret; + + if ((ret = bunyan_init("svp", &svp_bunyan)) != 0) + return (ret); + ret = bunyan_stream_add(svp_bunyan, "stderr", BUNYAN_L_INFO, + bunyan_stream_fd, (void *)STDERR_FILENO); + if (ret != 0) + bunyan_fini(svp_bunyan); + return (ret); +} + +static void +svp_bunyan_fini(void) +{ + if (svp_bunyan != NULL) + bunyan_fini(svp_bunyan); +} + +#pragma init(varpd_svp_init) +static void +varpd_svp_init(void) +{ + int err; + varpd_plugin_register_t *vpr; + + if (svp_bunyan_init() != 0) + return; + + if ((err = svp_host_init()) != 0) { + (void) bunyan_error(svp_bunyan, "failed to init host subsystem", + BUNYAN_T_INT32, "error", err, + BUNYAN_T_END); + svp_bunyan_fini(); + return; + } + + svp_lookup_cache = umem_cache_create("svp_lookup", + sizeof (svp_lookup_t), 0, NULL, NULL, NULL, NULL, NULL, 0); + if (svp_lookup_cache == NULL) { + (void) bunyan_error(svp_bunyan, + "failed to create svp_lookup cache", + BUNYAN_T_INT32, "error", errno, + BUNYAN_T_END); + svp_bunyan_fini(); + return; + } + + if ((err = svp_event_init()) != 0) { + (void) bunyan_error(svp_bunyan, + "failed to init event subsystem", + BUNYAN_T_INT32, "error", err, + BUNYAN_T_END); + svp_bunyan_fini(); + umem_cache_destroy(svp_lookup_cache); + return; + } + + if ((err = svp_timer_init()) != 0) { + (void) bunyan_error(svp_bunyan, + "failed to init timer subsystem", + BUNYAN_T_INT32, "error", err, + BUNYAN_T_END); + svp_event_fini(); + umem_cache_destroy(svp_lookup_cache); + svp_bunyan_fini(); + return; + } + + if ((err = svp_remote_init()) != 0) { + (void) bunyan_error(svp_bunyan, + "failed to init remote subsystem", + BUNYAN_T_INT32, "error", err, + BUNYAN_T_END); + svp_event_fini(); + umem_cache_destroy(svp_lookup_cache); + svp_bunyan_fini(); + return; + } + + vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err); + if (vpr == NULL) { + (void) bunyan_error(svp_bunyan, + "failed to alloc varpd plugin", + BUNYAN_T_INT32, "error", err, + BUNYAN_T_END); + svp_remote_fini(); + svp_event_fini(); + umem_cache_destroy(svp_lookup_cache); + svp_bunyan_fini(); + return; + } + + vpr->vpr_mode = OVERLAY_TARGET_DYNAMIC; + vpr->vpr_name = "svp"; + vpr->vpr_ops = &varpd_svp_ops; + + if ((err = libvarpd_plugin_register(vpr)) != 0) { + (void) bunyan_error(svp_bunyan, + "failed to register varpd plugin", + BUNYAN_T_INT32, "error", err, + BUNYAN_T_END); + svp_remote_fini(); + svp_event_fini(); + umem_cache_destroy(svp_lookup_cache); + svp_bunyan_fini(); + + } + libvarpd_plugin_free(vpr); +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp.h b/usr/src/lib/varpd/svp/common/libvarpd_svp.h new file mode 100644 index 0000000000..8192b842ce --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp.h @@ -0,0 +1,357 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_SVP_H +#define _LIBVARPD_SVP_H + +/* + * Implementation details of the SVP plugin and the SVP protocol. + */ + +#include <netinet/in.h> +#include <sys/ethernet.h> +#include <thread.h> +#include <synch.h> +#include <libvarpd_provider.h> +#include <sys/avl.h> +#include <port.h> +#include <sys/list.h> +#include <bunyan.h> + +#include <libvarpd_svp_prot.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct svp svp_t; +typedef struct svp_remote svp_remote_t; +typedef struct svp_conn svp_conn_t; +typedef struct svp_query svp_query_t; + +typedef void (*svp_event_f)(port_event_t *, void *); + +typedef struct svp_event { + svp_event_f se_func; + void *se_arg; + int se_events; +} svp_event_t; + +typedef void (*svp_timer_f)(void *); + +typedef struct svp_timer { + svp_timer_f st_func; /* Timer callback function */ + void *st_arg; /* Timer callback arg */ + boolean_t st_oneshot; /* Is timer a one shot? */ + uint32_t st_value; /* periodic or one-shot time */ + /* Fields below here are private to the svp_timer implementaiton */ + uint64_t st_expire; /* Next expiration */ + boolean_t st_delivering; /* Are we currently delivering this */ + avl_node_t st_link; +} svp_timer_t; + +/* + * Note, both the svp_log_ack_t and svp_lrm_req_t are not part of this structure + * as they are rather variable sized data and we don't want to constrain their + * size. Instead, the rdata and wdata members must be set appropriately. + */ +typedef union svp_query_data { + svp_vl2_req_t sqd_vl2r; + svp_vl2_ack_t sqd_vl2a; + svp_vl3_req_t sdq_vl3r; + svp_vl3_ack_t sdq_vl3a; + svp_log_req_t sdq_logr; + svp_lrm_ack_t sdq_lrma; +} svp_query_data_t; + +typedef void (*svp_query_f)(svp_query_t *, void *); + +typedef enum svp_query_state { + SVP_QUERY_INIT = 0x00, + SVP_QUERY_WRITING = 0x01, + SVP_QUERY_READING = 0x02, + SVP_QUERY_FINISHED = 0x03 +} svp_query_state_t; + +/* + * The query structure is usable for all forms of svp queries that end up + * getting passed across. Right now it's optimized for the fixed size data + * requests as opposed to requests whose responses will always be streaming in + * nature. Though, the streaming requests are the less common ones we have. We + * may need to make additional changes for those. + */ +struct svp_query { + list_node_t sq_lnode; /* List entry */ + svp_query_f sq_func; /* Callback function */ + svp_query_state_t sq_state; /* Query state */ + void *sq_arg; /* Callback function arg */ + svp_t *sq_svp; /* Pointer back to svp_t */ + svp_req_t sq_header; /* Header for the query */ + svp_query_data_t sq_rdun; /* Union for read data */ + svp_query_data_t sq_wdun; /* Union for write data */ + svp_status_t sq_status; /* Query response status */ + size_t sq_size; /* Query response size */ + void *sq_rdata; /* Read data pointer */ + size_t sq_rsize; /* Read data size */ + void *sq_wdata; /* Write data pointer */ + size_t sq_wsize; /* Write data size */ + hrtime_t sq_acttime; /* Last I/O activity time */ +}; + +typedef enum svp_conn_state { + SVP_CS_ERROR = 0x00, + SVP_CS_INITIAL = 0x01, + SVP_CS_CONNECTING = 0x02, + SVP_CS_BACKOFF = 0x03, + SVP_CS_ACTIVE = 0x04, + SVP_CS_WINDDOWN = 0x05 +} svp_conn_state_t; + +typedef enum svp_conn_error { + SVP_CE_NONE = 0x00, + SVP_CE_ASSOCIATE = 0x01, + SVP_CE_NOPOLLOUT = 0x02, + SVP_CE_SOCKET = 0x03 +} svp_conn_error_t; + +typedef enum svp_conn_flags { + SVP_CF_ADDED = 0x01, + SVP_CF_DEGRADED = 0x02, + SVP_CF_REAP = 0x04, + SVP_CF_TEARDOWN = 0x08, + SVP_CF_UFLAG = 0x0c, + SVP_CF_USER = 0x10 +} svp_conn_flags_t; + +typedef struct svp_conn_out { + svp_query_t *sco_query; + size_t sco_offset; +} svp_conn_out_t; + +typedef struct svp_conn_in { + svp_query_t *sci_query; + svp_req_t sci_req; + size_t sci_offset; +} svp_conn_in_t; + +struct svp_conn { + svp_remote_t *sc_remote; /* RO */ + struct in6_addr sc_addr; /* RO */ + list_node_t sc_rlist; /* svp_remote_t`sr_lock */ + mutex_t sc_lock; + svp_event_t sc_event; + svp_timer_t sc_btimer; + svp_timer_t sc_qtimer; + int sc_socket; + uint_t sc_gen; + uint_t sc_nbackoff; + svp_conn_flags_t sc_flags; + svp_conn_state_t sc_cstate; + svp_conn_error_t sc_error; + int sc_errno; + list_t sc_queries; + svp_conn_out_t sc_output; + svp_conn_in_t sc_input; +}; + +typedef enum svp_remote_state { + SVP_RS_LOOKUP_SCHEDULED = 0x01, /* On the DNS Queue */ + SVP_RS_LOOKUP_INPROGRESS = 0x02, /* Doing a DNS lookup */ + SVP_RS_LOOKUP_VALID = 0x04 /* addrinfo valid */ +} svp_remote_state_t; + +/* + * These series of bit-based flags should be ordered such that the most severe + * is first. We only can set one message that user land can see, so if more than + * one is set we want to make sure that one is there. + */ +typedef enum svp_degrade_state { + SVP_RD_DNS_FAIL = 0x01, /* DNS Resolution Failure */ + SVP_RD_REMOTE_FAIL = 0x02, /* cannot reach any remote peers */ + SVP_RD_ALL = 0x03 /* Only suitable for restore */ +} svp_degrade_state_t; + +typedef enum svp_shootdown_flags { + SVP_SD_RUNNING = 0x01, + SVP_SD_QUIESCE = 0x02, + SVP_SD_DORM = 0x04 +} svp_shootdown_flags_t; + +/* + * There is a single svp_sdlog_t per svp_remote_t. It maintains its own lock and + * condition variables. See the big theory statement for more information on how + * it's used. + */ +typedef struct svp_sdlog { + mutex_t sdl_lock; + cond_t sdl_cond; + uint_t sdl_ref; + svp_timer_t sdl_timer; + svp_shootdown_flags_t sdl_flags; + svp_query_t sdl_query; + void *sdl_logack; + void *sdl_logrm; + void *sdl_remote; +} svp_sdlog_t; + +struct svp_remote { + char *sr_hostname; /* RO */ + uint16_t sr_rport; /* RO */ + struct in6_addr sr_uip; /* RO */ + avl_node_t sr_gnode; /* svp_remote_lock */ + svp_remote_t *sr_nexthost; /* svp_host_lock */ + mutex_t sr_lock; + cond_t sr_cond; + svp_remote_state_t sr_state; + svp_degrade_state_t sr_degrade; + struct addrinfo *sr_addrinfo; + avl_tree_t sr_tree; + uint_t sr_count; /* active count */ + uint_t sr_gen; + uint_t sr_tconns; /* total conns + dconns */ + uint_t sr_ndconns; /* number of degraded conns */ + list_t sr_conns; /* all conns */ + svp_sdlog_t sr_shoot; +}; + +/* + * We have a bunch of different things that we get back from the API at the + * plug-in layer. These include: + * + * o OOB Shootdowns + * o VL3->VL2 Lookups + * o VL2->UL3 Lookups + * o VL2 Log invalidations + * o VL3 Log injections + */ +typedef void (*svp_vl2_lookup_f)(svp_t *, svp_status_t, const struct in6_addr *, + const uint16_t, void *); +typedef void (*svp_vl3_lookup_f)(svp_t *, svp_status_t, const uint8_t *, + const struct in6_addr *, const uint16_t, void *); +typedef void (*svp_vl2_invalidation_f)(svp_t *, const uint8_t *); +typedef void (*svp_vl3_inject_f)(svp_t *, const uint16_t, + const struct in6_addr *, const uint8_t *, const uint8_t *); +typedef void (*svp_shootdown_f)(svp_t *, const uint8_t *, + const struct in6_addr *, const uint16_t uport); + +typedef struct svp_cb { + svp_vl2_lookup_f scb_vl2_lookup; + svp_vl3_lookup_f scb_vl3_lookup; + svp_vl2_invalidation_f scb_vl2_invalidate; + svp_vl3_inject_f scb_vl3_inject; + svp_shootdown_f scb_shootdown; +} svp_cb_t; + +/* + * Core implementation structure. + */ +struct svp { + overlay_plugin_dest_t svp_dest; /* RO */ + varpd_provider_handle_t *svp_hdl; /* RO */ + svp_cb_t svp_cb; /* RO */ + uint64_t svp_vid; /* RO */ + avl_node_t svp_rlink; /* Owned by svp_remote */ + svp_remote_t *svp_remote; /* RO iff started */ + mutex_t svp_lock; + char *svp_host; /* svp_lock */ + uint16_t svp_port; /* svp_lock */ + uint16_t svp_uport; /* svp_lock */ + boolean_t svp_huip; /* svp_lock */ + struct in6_addr svp_uip; /* svp_lock */ +}; + +extern bunyan_logger_t *svp_bunyan; + +extern int svp_remote_find(char *, uint16_t, struct in6_addr *, + svp_remote_t **); +extern int svp_remote_attach(svp_remote_t *, svp_t *); +extern void svp_remote_detach(svp_t *); +extern void svp_remote_release(svp_remote_t *); +extern void svp_remote_vl3_lookup(svp_t *, svp_query_t *, + const struct sockaddr *, void *); +extern void svp_remote_vl2_lookup(svp_t *, svp_query_t *, const uint8_t *, + void *); + +/* + * Init functions + */ +extern int svp_remote_init(void); +extern void svp_remote_fini(void); +extern int svp_event_init(void); +extern int svp_event_timer_init(svp_event_t *); +extern void svp_event_fini(void); +extern int svp_host_init(void); +extern int svp_timer_init(void); + +/* + * Timers + */ +extern int svp_tickrate; +extern void svp_timer_add(svp_timer_t *); +extern void svp_timer_remove(svp_timer_t *); + +/* + * Event loop management + */ +extern int svp_event_associate(svp_event_t *, int); +extern int svp_event_dissociate(svp_event_t *, int); +extern int svp_event_inject(svp_event_t *); + +/* + * Connection manager + */ +extern int svp_conn_create(svp_remote_t *, const struct in6_addr *); +extern void svp_conn_destroy(svp_conn_t *); +extern void svp_conn_fallout(svp_conn_t *); +extern void svp_conn_queue(svp_conn_t *, svp_query_t *); + +/* + * FMA related + */ +extern void svp_remote_degrade(svp_remote_t *, svp_degrade_state_t); +extern void svp_remote_restore(svp_remote_t *, svp_degrade_state_t); + +/* + * Misc. + */ +extern int svp_comparator(const void *, const void *); +extern void svp_remote_reassign(svp_remote_t *, svp_conn_t *); +extern void svp_remote_resolved(svp_remote_t *, struct addrinfo *); +extern void svp_host_queue(svp_remote_t *); +extern void svp_query_release(svp_query_t *); +extern void svp_query_crc32(svp_req_t *, void *, size_t); + +/* + * Shootdown related + */ +extern void svp_remote_shootdown_vl3(svp_remote_t *, svp_log_vl3_t *, + svp_sdlog_t *); +extern void svp_remote_shootdown_vl2(svp_remote_t *, svp_log_vl2_t *); +extern void svp_remote_log_request(svp_remote_t *, svp_query_t *, void *, + size_t); +extern void svp_remote_lrm_request(svp_remote_t *, svp_query_t *, void *, + size_t); +extern void svp_shootdown_logr_cb(svp_remote_t *, svp_status_t, void *, size_t); +extern void svp_shootdown_lrm_cb(svp_remote_t *, svp_status_t); +extern void svp_shootdown_vl3_cb(svp_status_t, svp_log_vl3_t *, svp_sdlog_t *); +extern int svp_shootdown_init(svp_remote_t *); +extern void svp_shootdown_fini(svp_remote_t *); +extern void svp_shootdown_start(svp_remote_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_SVP_H */ diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c new file mode 100644 index 0000000000..4d10d1dba4 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c @@ -0,0 +1,1030 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Logic to manage an individual connection to a remote host. + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + */ + +#include <assert.h> +#include <umem.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/uio.h> +#include <sys/debug.h> + +#include <libvarpd_svp.h> + +int svp_conn_query_timeout = 30; +static int svp_conn_backoff_tbl[] = { 1, 2, 4, 8, 16, 32 }; +static int svp_conn_nbackoff = sizeof (svp_conn_backoff_tbl) / sizeof (int); + +typedef enum svp_conn_act { + SVP_RA_NONE = 0x00, + SVP_RA_DEGRADE = 0x01, + SVP_RA_RESTORE = 0x02, + SVP_RA_ERROR = 0x03, + SVP_RA_CLEANUP = 0x04 +} svp_conn_act_t; + +static void +svp_conn_inject(svp_conn_t *scp) +{ + int ret; + assert(MUTEX_HELD(&scp->sc_lock)); + + if (scp->sc_flags & SVP_CF_USER) + return; + scp->sc_flags |= SVP_CF_USER; + if ((ret = svp_event_inject(&scp->sc_event)) != 0) + libvarpd_panic("failed to inject event: %d\n", ret); +} + +static void +svp_conn_degrade(svp_conn_t *scp) +{ + svp_remote_t *srp = scp->sc_remote; + + assert(MUTEX_HELD(&srp->sr_lock)); + assert(MUTEX_HELD(&scp->sc_lock)); + + if (scp->sc_flags & SVP_CF_DEGRADED) + return; + + scp->sc_flags |= SVP_CF_DEGRADED; + srp->sr_ndconns++; + if (srp->sr_ndconns == srp->sr_tconns) + svp_remote_degrade(srp, SVP_RD_REMOTE_FAIL); +} + +static void +svp_conn_restore(svp_conn_t *scp) +{ + svp_remote_t *srp = scp->sc_remote; + + assert(MUTEX_HELD(&srp->sr_lock)); + assert(MUTEX_HELD(&scp->sc_lock)); + + if (!(scp->sc_flags & SVP_CF_DEGRADED)) + return; + + scp->sc_flags &= ~SVP_CF_DEGRADED; + if (srp->sr_ndconns == srp->sr_tconns) + svp_remote_restore(srp, SVP_RD_REMOTE_FAIL); + srp->sr_ndconns--; +} + +static void +svp_conn_add(svp_conn_t *scp) +{ + svp_remote_t *srp = scp->sc_remote; + + assert(MUTEX_HELD(&srp->sr_lock)); + assert(MUTEX_HELD(&scp->sc_lock)); + + if (scp->sc_flags & SVP_CF_ADDED) + return; + + list_insert_tail(&srp->sr_conns, scp); + scp->sc_flags |= SVP_CF_ADDED; + srp->sr_tconns++; +} + +static void +svp_conn_remove(svp_conn_t *scp) +{ + svp_remote_t *srp = scp->sc_remote; + + assert(MUTEX_HELD(&srp->sr_lock)); + assert(MUTEX_HELD(&scp->sc_lock)); + + if (!(scp->sc_flags & SVP_CF_ADDED)) + return; + + scp->sc_flags &= ~SVP_CF_ADDED; + if (scp->sc_flags & SVP_CF_DEGRADED) + srp->sr_ndconns--; + srp->sr_tconns--; + if (srp->sr_tconns == srp->sr_ndconns) + svp_remote_degrade(srp, SVP_RD_REMOTE_FAIL); +} + +static svp_query_t * +svp_conn_query_find(svp_conn_t *scp, uint32_t id) +{ + svp_query_t *sqp; + + assert(MUTEX_HELD(&scp->sc_lock)); + + for (sqp = list_head(&scp->sc_queries); sqp != NULL; + sqp = list_next(&scp->sc_queries, sqp)) { + if (sqp->sq_header.svp_id == id) + break; + } + + return (sqp); +} + +static svp_conn_act_t +svp_conn_backoff(svp_conn_t *scp) +{ + assert(MUTEX_HELD(&scp->sc_lock)); + + if (close(scp->sc_socket) != 0) + libvarpd_panic("failed to close socket %d: %d\n", + scp->sc_socket, errno); + scp->sc_socket = -1; + + scp->sc_cstate = SVP_CS_BACKOFF; + scp->sc_nbackoff++; + if (scp->sc_nbackoff >= svp_conn_nbackoff) { + scp->sc_btimer.st_value = + svp_conn_backoff_tbl[svp_conn_nbackoff - 1]; + } else { + scp->sc_btimer.st_value = + svp_conn_backoff_tbl[scp->sc_nbackoff - 1]; + } + svp_timer_add(&scp->sc_btimer); + + if (scp->sc_nbackoff > svp_conn_nbackoff) + return (SVP_RA_DEGRADE); + return (SVP_RA_NONE); +} + +static svp_conn_act_t +svp_conn_connect(svp_conn_t *scp) +{ + int ret; + struct sockaddr_in6 in6; + + assert(MUTEX_HELD(&scp->sc_lock)); + assert(scp->sc_cstate == SVP_CS_BACKOFF || + scp->sc_cstate == SVP_CS_INITIAL); + assert(scp->sc_socket == -1); + if (scp->sc_cstate == SVP_CS_INITIAL) + scp->sc_nbackoff = 0; + + scp->sc_socket = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (scp->sc_socket == -1) { + scp->sc_error = SVP_CE_SOCKET; + scp->sc_errno = errno; + scp->sc_cstate = SVP_CS_ERROR; + return (SVP_RA_DEGRADE); + } + + bzero(&in6, sizeof (struct sockaddr_in6)); + in6.sin6_family = AF_INET6; + in6.sin6_port = htons(scp->sc_remote->sr_rport); + bcopy(&scp->sc_addr, &in6.sin6_addr, sizeof (struct in6_addr)); + ret = connect(scp->sc_socket, (struct sockaddr *)&in6, + sizeof (struct sockaddr_in6)); + if (ret != 0) { + boolean_t async = B_FALSE; + + switch (errno) { + case EACCES: + case EADDRINUSE: + case EAFNOSUPPORT: + case EALREADY: + case EBADF: + case EISCONN: + case ELOOP: + case ENOENT: + case ENOSR: + case EWOULDBLOCK: + libvarpd_panic("unanticipated connect errno %d", errno); + break; + case EINPROGRESS: + case EINTR: + async = B_TRUE; + default: + break; + } + + /* + * So, we will be connecting to this in the future, advance our + * state and make sure that we poll for the next round. + */ + if (async == B_TRUE) { + scp->sc_cstate = SVP_CS_CONNECTING; + scp->sc_event.se_events = POLLOUT | POLLHUP; + ret = svp_event_associate(&scp->sc_event, + scp->sc_socket); + if (ret == 0) + return (SVP_RA_NONE); + scp->sc_error = SVP_CE_ASSOCIATE; + scp->sc_errno = ret; + scp->sc_cstate = SVP_CS_ERROR; + return (SVP_RA_DEGRADE); + } else { + /* + * This call failed, which means that we obtained one of + * the following: + * + * EADDRNOTAVAIL + * ECONNREFUSED + * EIO + * ENETUNREACH + * EHOSTUNREACH + * ENXIO + * ETIMEDOUT + * + * Therefore we need to set ourselves into backoff and + * wait for that to clear up. + */ + return (svp_conn_backoff(scp)); + } + } + + /* + * We've connected. Successfully move ourselves to the bound + * state and start polling. + */ + scp->sc_cstate = SVP_CS_ACTIVE; + scp->sc_event.se_events = POLLIN | POLLRDNORM | POLLHUP; + ret = svp_event_associate(&scp->sc_event, scp->sc_socket); + if (ret == 0) + return (SVP_RA_RESTORE); + scp->sc_error = SVP_CE_ASSOCIATE; + scp->sc_cstate = SVP_CS_ERROR; + + return (SVP_RA_DEGRADE); +} + +/* + * This should be the first call we get after a connect. If we have successfully + * connected, we should see a writeable event. We may also see an error or a + * hang up. In either of these cases, we transition to error mode. If there is + * also a readable event, we ignore it at the moment and just let a + * reassociation pick it up so we can simplify the set of state transitions that + * we have. + */ +static svp_conn_act_t +svp_conn_poll_connect(port_event_t *pe, svp_conn_t *scp) +{ + int ret, err; + socklen_t sl = sizeof (err); + if (!(pe->portev_events & POLLOUT)) { + scp->sc_errno = 0; + scp->sc_error = SVP_CE_NOPOLLOUT; + scp->sc_cstate = SVP_CS_ERROR; + return (SVP_RA_DEGRADE); + } + + ret = getsockopt(scp->sc_socket, SOL_SOCKET, SO_ERROR, &err, &sl); + if (ret != 0) + libvarpd_panic("unanticipated getsockopt error"); + if (err != 0) { + return (svp_conn_backoff(scp)); + } + + scp->sc_cstate = SVP_CS_ACTIVE; + scp->sc_event.se_events = POLLIN | POLLRDNORM | POLLHUP; + ret = svp_event_associate(&scp->sc_event, scp->sc_socket); + if (ret == 0) + return (SVP_RA_RESTORE); + scp->sc_error = SVP_CE_ASSOCIATE; + scp->sc_errno = ret; + scp->sc_cstate = SVP_CS_ERROR; + return (SVP_RA_DEGRADE); +} + +static svp_conn_act_t +svp_conn_pollout(svp_conn_t *scp) +{ + svp_query_t *sqp; + svp_req_t *req; + size_t off; + struct iovec iov[2]; + int nvecs = 0; + ssize_t ret; + + assert(MUTEX_HELD(&scp->sc_lock)); + + /* + * We need to find a query and start writing it out. + */ + if (scp->sc_output.sco_query == NULL) { + for (sqp = list_head(&scp->sc_queries); sqp != NULL; + sqp = list_next(&scp->sc_queries, sqp)) { + if (sqp->sq_state != SVP_QUERY_INIT) + continue; + break; + } + + if (sqp == NULL) { + scp->sc_event.se_events &= ~POLLOUT; + return (SVP_RA_NONE); + } + + scp->sc_output.sco_query = sqp; + scp->sc_output.sco_offset = 0; + sqp->sq_state = SVP_QUERY_WRITING; + svp_query_crc32(&sqp->sq_header, sqp->sq_rdata, sqp->sq_rsize); + } + + sqp = scp->sc_output.sco_query; + req = &sqp->sq_header; + off = scp->sc_output.sco_offset; + if (off < sizeof (svp_req_t)) { + iov[nvecs].iov_base = (void *)((uintptr_t)req + off); + iov[nvecs].iov_len = sizeof (svp_req_t) - off; + nvecs++; + off = 0; + } else { + off -= sizeof (svp_req_t); + } + + iov[nvecs].iov_base = (void *)((uintptr_t)sqp->sq_rdata + off); + iov[nvecs].iov_len = sqp->sq_rsize - off; + nvecs++; + + do { + ret = writev(scp->sc_socket, iov, nvecs); + } while (ret == -1 && errno == EAGAIN); + if (ret == -1) { + switch (errno) { + case EAGAIN: + scp->sc_event.se_events |= POLLOUT; + return (SVP_RA_NONE); + case EIO: + case ENXIO: + case ECONNRESET: + return (SVP_RA_ERROR); + default: + libvarpd_panic("unexpected errno: %d", errno); + } + } + + sqp->sq_acttime = gethrtime(); + scp->sc_output.sco_offset += ret; + if (ret >= sizeof (svp_req_t) + sqp->sq_rsize) { + sqp->sq_state = SVP_QUERY_READING; + scp->sc_output.sco_query = NULL; + scp->sc_output.sco_offset = 0; + scp->sc_event.se_events |= POLLOUT; + } + return (SVP_RA_NONE); +} + +static boolean_t +svp_conn_pollin_validate(svp_conn_t *scp) +{ + svp_query_t *sqp; + uint32_t nsize; + uint16_t nvers, nop; + svp_req_t *resp = &scp->sc_input.sci_req; + + assert(MUTEX_HELD(&scp->sc_lock)); + + nvers = ntohs(resp->svp_ver); + nop = ntohs(resp->svp_op); + nsize = ntohl(resp->svp_size); + + if (nvers != SVP_CURRENT_VERSION) { + (void) bunyan_warn(svp_bunyan, "unsupported version", + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", nvers, + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response_id", resp->svp_id, + BUNYAN_T_END); + return (B_FALSE); + } + + if (nop != SVP_R_VL2_ACK && nop != SVP_R_VL3_ACK && + nop != SVP_R_LOG_ACK && nop != SVP_R_LOG_RM_ACK) { + (void) bunyan_warn(svp_bunyan, "unsupported operation", + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", nvers, + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response_id", resp->svp_id, + BUNYAN_T_END); + return (B_FALSE); + } + + sqp = svp_conn_query_find(scp, resp->svp_id); + if (sqp == NULL) { + (void) bunyan_warn(svp_bunyan, "unknown response id", + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", nvers, + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response_id", resp->svp_id, + BUNYAN_T_END); + return (B_FALSE); + } + + if (sqp->sq_state != SVP_QUERY_READING) { + (void) bunyan_warn(svp_bunyan, + "got response for unexpecting query", + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", nvers, + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response_id", resp->svp_id, + BUNYAN_T_INT32, "query_state", sqp->sq_state, + BUNYAN_T_END); + return (B_FALSE); + } + + if ((nop == SVP_R_VL2_ACK && nsize != sizeof (svp_vl2_ack_t)) || + (nop == SVP_R_VL3_ACK && nsize != sizeof (svp_vl3_ack_t)) || + (nop == SVP_R_LOG_RM_ACK && nsize != sizeof (svp_lrm_ack_t))) { + (void) bunyan_warn(svp_bunyan, "response size too large", + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", nvers, + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response_id", resp->svp_id, + BUNYAN_T_INT32, "response_size", nsize, + BUNYAN_T_INT32, "expected_size", nop == SVP_R_VL2_ACK ? + sizeof (svp_vl2_ack_t) : sizeof (svp_vl3_ack_t), + BUNYAN_T_INT32, "query_state", sqp->sq_state, + BUNYAN_T_END); + return (B_FALSE); + } + + /* + * The valid size is anything <= to what the user requested, but at + * least svp_log_ack_t bytes large. + */ + if (nop == SVP_R_LOG_ACK) { + const char *msg = NULL; + if (nsize < sizeof (svp_log_ack_t)) + msg = "response size too small"; + else if (nsize > ((svp_log_req_t *)sqp->sq_rdata)->svlr_count) + msg = "response size too large"; + if (msg != NULL) { + (void) bunyan_warn(svp_bunyan, msg, + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", + scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", nvers, + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response_id", resp->svp_id, + BUNYAN_T_INT32, "response_size", nsize, + BUNYAN_T_INT32, "expected_size", + ((svp_log_req_t *)sqp->sq_rdata)->svlr_count, + BUNYAN_T_INT32, "query_state", sqp->sq_state, + BUNYAN_T_END); + return (B_FALSE); + } + } + + sqp->sq_size = nsize; + scp->sc_input.sci_query = sqp; + if (nop == SVP_R_VL2_ACK || nop == SVP_R_VL3_ACK || + nop == SVP_R_LOG_RM_ACK) { + sqp->sq_wdata = &sqp->sq_wdun; + sqp->sq_wsize = sizeof (svp_query_data_t); + } else { + VERIFY(nop == SVP_R_LOG_ACK); + assert(sqp->sq_wdata != NULL); + assert(sqp->sq_wsize != 0); + } + + return (B_TRUE); +} + +static svp_conn_act_t +svp_conn_pollin(svp_conn_t *scp) +{ + size_t off, total; + ssize_t ret; + svp_query_t *sqp; + uint32_t crc; + uint16_t nop; + + assert(MUTEX_HELD(&scp->sc_lock)); + + /* + * No query implies that we're reading in the header and that the offset + * is associted with it. + */ + off = scp->sc_input.sci_offset; + sqp = scp->sc_input.sci_query; + if (scp->sc_input.sci_query == NULL) { + svp_req_t *resp = &scp->sc_input.sci_req; + + assert(off < sizeof (svp_req_t)); + + do { + ret = read(scp->sc_socket, + (void *)((uintptr_t)resp + off), + sizeof (svp_req_t) - off); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + switch (errno) { + case EAGAIN: + scp->sc_event.se_events |= POLLIN | POLLRDNORM; + return (SVP_RA_NONE); + case EIO: + case ECONNRESET: + return (SVP_RA_ERROR); + break; + default: + libvarpd_panic("unexpeted read errno: %d", + errno); + } + } else if (ret == 0) { + /* Try to reconnect to the remote host */ + return (SVP_RA_ERROR); + } + + /* Didn't get all the data we need */ + if (off + ret < sizeof (svp_req_t)) { + scp->sc_input.sci_offset += ret; + scp->sc_event.se_events |= POLLIN | POLLRDNORM; + return (SVP_RA_NONE); + } + + if (svp_conn_pollin_validate(scp) != B_TRUE) + return (SVP_RA_ERROR); + } + + sqp = scp->sc_input.sci_query; + assert(sqp != NULL); + sqp->sq_acttime = gethrtime(); + total = ntohl(scp->sc_input.sci_req.svp_size); + do { + ret = read(scp->sc_socket, + (void *)((uintptr_t)sqp->sq_wdata + off), + total - off); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + switch (errno) { + case EAGAIN: + scp->sc_event.se_events |= POLLIN | POLLRDNORM; + return (SVP_RA_NONE); + case EIO: + case ECONNRESET: + return (SVP_RA_ERROR); + break; + default: + libvarpd_panic("unexpeted read errno: %d", errno); + } + } else if (ret == 0) { + /* Try to reconnect to the remote host */ + return (SVP_RA_ERROR); + } + + if (ret + off < total) { + scp->sc_input.sci_offset += ret; + return (SVP_RA_NONE); + } + + nop = ntohs(scp->sc_input.sci_req.svp_op); + crc = scp->sc_input.sci_req.svp_crc32; + svp_query_crc32(&scp->sc_input.sci_req, sqp->sq_wdata, total); + if (crc != scp->sc_input.sci_req.svp_crc32) { + (void) bunyan_info(svp_bunyan, "crc32 mismatch", + BUNYAN_T_IP, "remote ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "version", + ntohs(scp->sc_input.sci_req.svp_ver), + BUNYAN_T_INT32, "operation", nop, + BUNYAN_T_INT32, "response id", + ntohl(scp->sc_input.sci_req.svp_id), + BUNYAN_T_INT32, "query state", sqp->sq_state, + BUNYAN_T_UINT32, "msg_crc", ntohl(crc), + BUNYAN_T_UINT32, "calc_crc", + ntohl(scp->sc_input.sci_req.svp_crc32), + BUNYAN_T_END); + return (SVP_RA_ERROR); + } + scp->sc_input.sci_query = NULL; + scp->sc_input.sci_offset = 0; + + if (nop == SVP_R_VL2_ACK) { + svp_vl2_ack_t *sl2a = sqp->sq_wdata; + sqp->sq_status = ntohl(sl2a->sl2a_status); + } else if (nop == SVP_R_VL3_ACK) { + svp_vl3_ack_t *sl3a = sqp->sq_wdata; + sqp->sq_status = ntohl(sl3a->sl3a_status); + } else if (nop == SVP_R_LOG_ACK) { + svp_log_ack_t *svla = sqp->sq_wdata; + sqp->sq_status = ntohl(svla->svla_status); + } else if (nop == SVP_R_LOG_RM_ACK) { + svp_lrm_ack_t *svra = sqp->sq_wdata; + sqp->sq_status = ntohl(svra->svra_status); + } else { + libvarpd_panic("unhandled nop: %d", nop); + } + + list_remove(&scp->sc_queries, sqp); + mutex_exit(&scp->sc_lock); + + /* + * We have to release all of our resources associated with this entry + * before we call the callback. After we call it, the memory will be + * lost to time. + */ + svp_query_release(sqp); + sqp->sq_func(sqp, sqp->sq_arg); + mutex_enter(&scp->sc_lock); + scp->sc_event.se_events |= POLLIN | POLLRDNORM; + + return (SVP_RA_NONE); +} + +static svp_conn_act_t +svp_conn_reset(svp_conn_t *scp) +{ + svp_remote_t *srp = scp->sc_remote; + + assert(MUTEX_HELD(&srp->sr_lock)); + assert(MUTEX_HELD(&scp->sc_lock)); + + assert(svp_event_dissociate(&scp->sc_event, scp->sc_socket) == + ENOENT); + if (close(scp->sc_socket) != 0) + libvarpd_panic("failed to close socket %d: %d", scp->sc_socket, + errno); + scp->sc_flags &= ~SVP_CF_TEARDOWN; + scp->sc_socket = -1; + scp->sc_cstate = SVP_CS_INITIAL; + scp->sc_input.sci_query = NULL; + scp->sc_output.sco_query = NULL; + + svp_remote_reassign(srp, scp); + + return (svp_conn_connect(scp)); +} + +/* + * This is our general state transition function. We're called here when we want + * to advance part of our state machine as well as to re-arm ourselves. We can + * also end up here from the standard event loop as a result of having a user + * event posted. + */ +static void +svp_conn_handler(port_event_t *pe, void *arg) +{ + svp_conn_t *scp = arg; + svp_remote_t *srp = scp->sc_remote; + svp_conn_act_t ret = SVP_RA_NONE; + svp_conn_state_t oldstate; + + mutex_enter(&scp->sc_lock); + + /* + * Check if one of our event interrupts is set. An event interrupt, such + * as having to be reaped or be torndown is notified by a + * PORT_SOURCE_USER event that tries to take care of this. However, + * because of the fact that the event loop can be ongoing despite this, + * we may get here before the PORT_SOURCE_USER has casued us to get + * here. In such a case, if the PORT_SOURCE_USER event is tagged, then + * we're going to opt to do nothing here and wait for it to come and + * tear us down. That will also indicate to us that we have nothing to + * worry about as far as general timing and the like goes. + */ + if ((scp->sc_flags & SVP_CF_UFLAG) != 0 && + (scp->sc_flags & SVP_CF_USER) != 0 && + pe != NULL && + pe->portev_source != PORT_SOURCE_USER) { + mutex_exit(&scp->sc_lock); + return; + } + + if (pe != NULL && pe->portev_source == PORT_SOURCE_USER) { + scp->sc_flags &= ~SVP_CF_USER; + if ((scp->sc_flags & SVP_CF_UFLAG) == 0) { + mutex_exit(&scp->sc_lock); + return; + } + } + + /* Check if this needs to be freed */ + if (scp->sc_flags & SVP_CF_REAP) { + mutex_exit(&scp->sc_lock); + svp_conn_destroy(scp); + return; + } + + /* Check if this needs to be reset */ + if (scp->sc_flags & SVP_CF_TEARDOWN) { + /* Make sure any other users of this are disassociated */ + ret = SVP_RA_ERROR; + goto out; + } + + switch (scp->sc_cstate) { + case SVP_CS_INITIAL: + case SVP_CS_BACKOFF: + assert(pe == NULL); + ret = svp_conn_connect(scp); + break; + case SVP_CS_CONNECTING: + assert(pe != NULL); + ret = svp_conn_poll_connect(pe, scp); + break; + case SVP_CS_ACTIVE: + case SVP_CS_WINDDOWN: + assert(pe != NULL); + oldstate = scp->sc_cstate; + if (pe->portev_events & POLLOUT) + ret = svp_conn_pollout(scp); + if (ret == SVP_RA_NONE && (pe->portev_events & POLLIN)) + ret = svp_conn_pollin(scp); + + if (oldstate == SVP_CS_WINDDOWN && + (list_is_empty(&scp->sc_queries) || ret != SVP_RA_NONE)) { + ret = SVP_RA_CLEANUP; + } + + if (ret == SVP_RA_NONE) { + int err; + if ((err = svp_event_associate(&scp->sc_event, + scp->sc_socket)) != 0) { + scp->sc_error = SVP_CE_ASSOCIATE; + scp->sc_errno = err; + scp->sc_cstate = SVP_CS_ERROR; + ret = SVP_RA_DEGRADE; + } + } + break; + default: + libvarpd_panic("svp_conn_handler encountered unexpected " + "state: %d", scp->sc_cstate); + } +out: + mutex_exit(&scp->sc_lock); + + if (ret == SVP_RA_NONE) + return; + + mutex_enter(&srp->sr_lock); + mutex_enter(&scp->sc_lock); + if (ret == SVP_RA_ERROR) + ret = svp_conn_reset(scp); + + if (ret == SVP_RA_DEGRADE) + svp_conn_degrade(scp); + if (ret == SVP_RA_RESTORE) + svp_conn_restore(scp); + + if (ret == SVP_RA_CLEANUP) { + svp_conn_remove(scp); + scp->sc_flags |= SVP_CF_REAP; + svp_conn_inject(scp); + } + mutex_exit(&scp->sc_lock); + mutex_exit(&srp->sr_lock); +} + +static void +svp_conn_backtimer(void *arg) +{ + svp_conn_t *scp = arg; + + svp_conn_handler(NULL, scp); +} + +/* + * This fires every svp_conn_query_timeout seconds. Its purpos is to determine + * if we haven't heard back on a request with in svp_conn_query_timeout seconds. + * If any of the svp_conn_query_t's that have been started (indicated by + * svp_query_t`sq_acttime != -1), and more than svp_conn_query_timeout seconds + * have passed, we basically tear this connection down and reassign outstanding + * queries. + */ +static void +svp_conn_querytimer(void *arg) +{ + int ret; + svp_query_t *sqp; + svp_conn_t *scp = arg; + hrtime_t now = gethrtime(); + + mutex_enter(&scp->sc_lock); + + /* + * If we're not in the active state, then we don't care about this as + * we're already either going to die or we have no connections to worry + * about. + */ + if (scp->sc_cstate != SVP_CS_ACTIVE) { + mutex_exit(&scp->sc_lock); + return; + } + + for (sqp = list_head(&scp->sc_queries); sqp != NULL; + sqp = list_next(&scp->sc_queries, sqp)) { + if (sqp->sq_acttime == -1) + continue; + if ((now - sqp->sq_acttime) / NANOSEC > svp_conn_query_timeout) + break; + } + + /* Nothing timed out, we're good here */ + if (sqp == NULL) { + mutex_exit(&scp->sc_lock); + return; + } + + (void) bunyan_warn(svp_bunyan, "query timed out on connection", + BUNYAN_T_IP, "remote_ip", &scp->sc_addr, + BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport, + BUNYAN_T_INT32, "operation", ntohs(sqp->sq_header.svp_op), + BUNYAN_T_END); + + /* + * Begin the tear down process for this connect. If we lose the + * disassociate, then we don't inject an event. See the big theory + * statement in libvarpd_svp.c for more information. + */ + scp->sc_flags |= SVP_CF_TEARDOWN; + + ret = svp_event_dissociate(&scp->sc_event, scp->sc_socket); + if (ret == 0) + svp_conn_inject(scp); + else + VERIFY(ret == ENOENT); + + mutex_exit(&scp->sc_lock); +} + +/* + * This connection has fallen out of DNS, figure out what we need to do with it. + */ +void +svp_conn_fallout(svp_conn_t *scp) +{ + svp_remote_t *srp = scp->sc_remote; + + assert(MUTEX_HELD(&srp->sr_lock)); + + mutex_enter(&scp->sc_lock); + switch (scp->sc_cstate) { + case SVP_CS_ERROR: + /* + * Connection is already inactive, so it's safe to tear down. + * Fire it off through the state machine to tear down via the + * backoff timer. + */ + svp_conn_remove(scp); + scp->sc_flags |= SVP_CF_REAP; + svp_conn_inject(scp); + break; + case SVP_CS_INITIAL: + case SVP_CS_BACKOFF: + case SVP_CS_CONNECTING: + /* + * Here, we have something actively going on, so we'll let it be + * clean up the next time we hit the event loop by the event + * loop itself. As it has no connections, there isn't much to + * really do, though we'll take this chance to go ahead and + * remove it from the remote. + */ + svp_conn_remove(scp); + scp->sc_flags |= SVP_CF_REAP; + svp_conn_inject(scp); + break; + case SVP_CS_ACTIVE: + case SVP_CS_WINDDOWN: + /* + * If there are no outstanding queries, then we should simply + * clean this up now,t he same way we would with the others. + * Othewrise, as we know the event loop is ongoing, we'll make + * sure that these entries get cleaned up once they're done. + */ + scp->sc_cstate = SVP_CS_WINDDOWN; + if (list_is_empty(&scp->sc_queries)) { + svp_conn_remove(scp); + scp->sc_flags |= SVP_CF_REAP; + svp_conn_inject(scp); + } + break; + default: + libvarpd_panic("svp_conn_fallout encountered" + "unkonwn state"); + } + mutex_exit(&scp->sc_lock); +} + +int +svp_conn_create(svp_remote_t *srp, const struct in6_addr *addr) +{ + int ret; + svp_conn_t *scp; + + assert(MUTEX_HELD(&srp->sr_lock)); + scp = umem_zalloc(sizeof (svp_conn_t), UMEM_DEFAULT); + if (scp == NULL) + return (ENOMEM); + + if ((ret = mutex_init(&scp->sc_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL)) != 0) { + umem_free(scp, sizeof (svp_conn_t)); + return (ret); + } + + scp->sc_remote = srp; + scp->sc_event.se_func = svp_conn_handler; + scp->sc_event.se_arg = scp; + scp->sc_btimer.st_func = svp_conn_backtimer; + scp->sc_btimer.st_arg = scp; + scp->sc_btimer.st_oneshot = B_TRUE; + scp->sc_btimer.st_value = 1; + + scp->sc_qtimer.st_func = svp_conn_querytimer; + scp->sc_qtimer.st_arg = scp; + scp->sc_qtimer.st_oneshot = B_FALSE; + scp->sc_qtimer.st_value = svp_conn_query_timeout; + + scp->sc_socket = -1; + + list_create(&scp->sc_queries, sizeof (svp_query_t), + offsetof(svp_query_t, sq_lnode)); + scp->sc_gen = srp->sr_gen; + bcopy(addr, &scp->sc_addr, sizeof (struct in6_addr)); + scp->sc_cstate = SVP_CS_INITIAL; + mutex_enter(&scp->sc_lock); + svp_conn_add(scp); + mutex_exit(&scp->sc_lock); + + /* Now that we're locked and loaded, add our timers */ + svp_timer_add(&scp->sc_qtimer); + svp_timer_add(&scp->sc_btimer); + + return (0); +} + +/* + * At the time of calling, the entry has been removed from all lists. In + * addition, the entries state should be SVP_CS_ERROR, therefore, we know that + * the fd should not be associated with the event loop. We'll double check that + * just in case. We should also have already been removed from the remote's + * list. + */ +void +svp_conn_destroy(svp_conn_t *scp) +{ + int ret; + + mutex_enter(&scp->sc_lock); + if (scp->sc_cstate != SVP_CS_ERROR) + libvarpd_panic("asked to tear down an active connection"); + if (scp->sc_flags & SVP_CF_ADDED) + libvarpd_panic("asked to remove a connection still in " + "the remote list\n"); + if (!list_is_empty(&scp->sc_queries)) + libvarpd_panic("asked to remove a connection with non-empty " + "query list"); + + if ((ret = svp_event_dissociate(&scp->sc_event, scp->sc_socket)) != + ENOENT) { + libvarpd_panic("dissociate failed or was actually " + "associated: %d", ret); + } + mutex_exit(&scp->sc_lock); + + /* Verify our timers are killed */ + svp_timer_remove(&scp->sc_btimer); + svp_timer_remove(&scp->sc_qtimer); + + if (scp->sc_socket != -1 && close(scp->sc_socket) != 0) + libvarpd_panic("failed to close svp_conn_t`scp_socket fd " + "%d: %d", scp->sc_socket, errno); + + list_destroy(&scp->sc_queries); + umem_free(scp, sizeof (svp_conn_t)); +} + +void +svp_conn_queue(svp_conn_t *scp, svp_query_t *sqp) +{ + assert(MUTEX_HELD(&scp->sc_lock)); + assert(scp->sc_cstate == SVP_CS_ACTIVE); + + sqp->sq_acttime = -1; + list_insert_tail(&scp->sc_queries, sqp); + if (!(scp->sc_event.se_events & POLLOUT)) { + scp->sc_event.se_events |= POLLOUT; + /* + * If this becomes frequent, we should instead give up on this + * set of connections instead of aborting. + */ + if (svp_event_associate(&scp->sc_event, scp->sc_socket) != 0) + libvarpd_panic("svp_event_associate failed somehow"); + } +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c new file mode 100644 index 0000000000..ade47ff998 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c @@ -0,0 +1,53 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + +/* + * Perform standard crc32 functions. + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + * + * Really, this should just be a libcrc. + */ + +#include <sys/crc32.h> +#include <stdint.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <inttypes.h> +#include <libvarpd_svp.h> + +static uint32_t svp_crc32_tab[] = { CRC32_TABLE }; + +static uint32_t +svp_crc32(uint32_t old, const uint8_t *buf, size_t len) +{ + uint32_t out; + + CRC32(out, buf, len, old, svp_crc32_tab); + return (out); +} + +void +svp_query_crc32(svp_req_t *shp, void *buf, size_t data) +{ + uint32_t crc = -1U; + + shp->svp_crc32 = 0; + crc = svp_crc32(crc, (uint8_t *)shp, sizeof (svp_req_t)); + crc = svp_crc32(crc, buf, data); + crc = ~crc; + shp->svp_crc32 = htonl(crc); +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_host.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_host.c new file mode 100644 index 0000000000..e91cc30e9d --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_host.c @@ -0,0 +1,171 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * DNS Host-name related functions. + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + */ + +#include <sys/socket.h> +#include <netdb.h> +#include <thread.h> +#include <synch.h> +#include <assert.h> +#include <errno.h> + +#include <libvarpd_svp.h> + +int svp_host_nthreads = 8; + +static mutex_t svp_host_lock = ERRORCHECKMUTEX; +static cond_t svp_host_cv = DEFAULTCV; +static svp_remote_t *svp_host_head; + +/* ARGSUSED */ +static void * +svp_host_loop(void *unused) +{ + for (;;) { + int err; + svp_remote_t *srp; + struct addrinfo *addrs; + + mutex_enter(&svp_host_lock); + while (svp_host_head == NULL) + (void) cond_wait(&svp_host_cv, &svp_host_lock); + srp = svp_host_head; + svp_host_head = srp->sr_nexthost; + if (svp_host_head != NULL) + (void) cond_signal(&svp_host_cv); + mutex_exit(&svp_host_lock); + + mutex_enter(&srp->sr_lock); + assert(srp->sr_state & SVP_RS_LOOKUP_SCHEDULED); + srp->sr_state &= ~SVP_RS_LOOKUP_SCHEDULED; + if (srp->sr_state & SVP_RS_LOOKUP_INPROGRESS) { + mutex_exit(&srp->sr_lock); + continue; + } + srp->sr_state |= SVP_RS_LOOKUP_INPROGRESS; + mutex_exit(&srp->sr_lock); + + for (;;) { + err = getaddrinfo(srp->sr_hostname, NULL, NULL, &addrs); + if (err == 0) + break; + if (err != 0) { + switch (err) { + case EAI_ADDRFAMILY: + case EAI_BADFLAGS: + case EAI_FAMILY: + case EAI_SERVICE: + case EAI_SOCKTYPE: + case EAI_OVERFLOW: + default: + libvarpd_panic("unexpected getaddrinfo " + "failure: %d", err); + break; + case EAI_AGAIN: + case EAI_MEMORY: + case EAI_SYSTEM: + continue; + case EAI_FAIL: + case EAI_NODATA: + case EAI_NONAME: + /* + * At this point in time we have + * something which isn't very good. This + * may have been a typo or something may + * have been destroyed. We should go + * ahead and degrade this overall + * instance, because we're not going to + * make much forward progress... It'd be + * great if we could actually issue more + * of an EREPORT to describe what + * happened, some day. + */ + mutex_enter(&srp->sr_lock); + svp_remote_degrade(srp, + SVP_RD_DNS_FAIL); + mutex_exit(&srp->sr_lock); + break; + } + } + break; + } + + if (err == 0) { + /* + * We've successfully resolved something, mark this + * degredation over for now. + */ + mutex_enter(&srp->sr_lock); + svp_remote_restore(srp, SVP_RD_DNS_FAIL); + mutex_exit(&srp->sr_lock); + svp_remote_resolved(srp, addrs); + } + + mutex_enter(&srp->sr_lock); + srp->sr_state &= ~SVP_RS_LOOKUP_INPROGRESS; + (void) cond_broadcast(&srp->sr_cond); + mutex_exit(&srp->sr_lock); + } + + /* LINTED: E_STMT_NOT_REACHED */ + return (NULL); +} + +void +svp_host_queue(svp_remote_t *srp) +{ + svp_remote_t *s; + mutex_enter(&svp_host_lock); + mutex_enter(&srp->sr_lock); + if (srp->sr_state & SVP_RS_LOOKUP_SCHEDULED) { + mutex_exit(&srp->sr_lock); + mutex_exit(&svp_host_lock); + return; + } + srp->sr_state |= SVP_RS_LOOKUP_SCHEDULED; + s = svp_host_head; + while (s != NULL && s->sr_nexthost != NULL) + s = s->sr_nexthost; + if (s == NULL) { + assert(s == svp_host_head); + svp_host_head = srp; + } else { + s->sr_nexthost = srp; + } + srp->sr_nexthost = NULL; + (void) cond_signal(&svp_host_cv); + mutex_exit(&srp->sr_lock); + mutex_exit(&svp_host_lock); +} + +int +svp_host_init(void) +{ + int i; + + for (i = 0; i < svp_host_nthreads; i++) { + if (thr_create(NULL, 0, svp_host_loop, NULL, + THR_DETACHED | THR_DAEMON, NULL) != 0) + return (errno); + } + + return (0); +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c new file mode 100644 index 0000000000..18a79b9dff --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c @@ -0,0 +1,210 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Event loop mechanism for our backend. + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + */ + +#include <unistd.h> +#include <thread.h> +#include <port.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <umem.h> + +#include <libvarpd_svp.h> + +typedef struct svp_event_loop { + int sel_port; /* RO */ + int sel_nthread; /* RO */ + thread_t *sel_threads; /* RO */ + boolean_t sel_stop; /* svp_elock */ + timer_t sel_hosttimer; +} svp_event_loop_t; + +static svp_event_loop_t svp_event; +static mutex_t svp_elock = ERRORCHECKMUTEX; + +/* ARGSUSED */ +static void * +svp_event_thr(void *arg) +{ + for (;;) { + int ret; + port_event_t pe; + svp_event_t *sep; + + mutex_enter(&svp_elock); + if (svp_event.sel_stop == B_TRUE) { + mutex_exit(&svp_elock); + break; + } + mutex_exit(&svp_elock); + + ret = port_get(svp_event.sel_port, &pe, NULL); + if (ret != 0) { + switch (errno) { + case EFAULT: + case EBADF: + case EINVAL: + libvarpd_panic("unexpected port_get errno: %d", + errno); + default: + break; + } + } + + if (pe.portev_user == NULL) + libvarpd_panic("received event (%p) without " + "protev_user set", &pe); + sep = (svp_event_t *)pe.portev_user; + sep->se_func(&pe, sep->se_arg); + } + + return (NULL); +} + +int +svp_event_associate(svp_event_t *sep, int fd) +{ + int ret; + + ret = port_associate(svp_event.sel_port, PORT_SOURCE_FD, fd, + sep->se_events, sep); + if (ret != 0) { + switch (errno) { + case EBADF: + case EBADFD: + case EINVAL: + case EAGAIN: + libvarpd_panic("unexpected port_associate error: %d", + errno); + default: + ret = errno; + break; + } + } + + return (ret); +} + +/* ARGSUSED */ +int +svp_event_dissociate(svp_event_t *sep, int fd) +{ + int ret; + + ret = port_dissociate(svp_event.sel_port, PORT_SOURCE_FD, fd); + if (ret != 0) { + if (errno != ENOENT) + libvarpd_panic("unexpected port_dissociate error: %d", + errno); + ret = errno; + } + return (ret); +} + +int +svp_event_inject(svp_event_t *user) +{ + return (port_send(svp_event.sel_port, 0, user)); +} + +int +svp_event_timer_init(svp_event_t *sep) +{ + port_notify_t pn; + struct sigevent evp; + struct itimerspec ts; + + pn.portnfy_port = svp_event.sel_port; + pn.portnfy_user = sep; + evp.sigev_notify = SIGEV_PORT; + evp.sigev_value.sival_ptr = &pn; + + if (timer_create(CLOCK_REALTIME, &evp, &svp_event.sel_hosttimer) != 0) + return (errno); + + ts.it_value.tv_sec = svp_tickrate; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = svp_tickrate; + ts.it_interval.tv_nsec = 0; + + if (timer_settime(svp_event.sel_hosttimer, TIMER_RELTIME, &ts, + NULL) != 0) { + int ret = errno; + (void) timer_delete(svp_event.sel_hosttimer); + return (ret); + } + + return (0); +} + +int +svp_event_init(void) +{ + long i, ncpus; + + svp_event.sel_port = port_create(); + if (svp_event.sel_port == -1) + return (errno); + + ncpus = sysconf(_SC_NPROCESSORS_ONLN) * 2 + 1; + if (ncpus <= 0) + libvarpd_panic("sysconf for nprocs failed... %d/%d", + ncpus, errno); + + svp_event.sel_threads = umem_alloc(sizeof (thread_t) * ncpus, + UMEM_DEFAULT); + if (svp_event.sel_threads == NULL) { + int ret = errno; + (void) timer_delete(svp_event.sel_hosttimer); + (void) close(svp_event.sel_port); + svp_event.sel_port = -1; + return (ret); + } + + for (i = 0; i < ncpus; i++) { + int ret; + thread_t *thr = &svp_event.sel_threads[i]; + + ret = thr_create(NULL, 0, svp_event_thr, NULL, + THR_DETACHED | THR_DAEMON, thr); + if (ret != 0) { + ret = errno; + (void) timer_delete(svp_event.sel_hosttimer); + (void) close(svp_event.sel_port); + svp_event.sel_port = -1; + return (errno); + } + } + + return (0); +} + +void +svp_event_fini(void) +{ + mutex_enter(&svp_elock); + svp_event.sel_stop = B_TRUE; + mutex_exit(&svp_elock); + + (void) timer_delete(svp_event.sel_hosttimer); + (void) close(svp_event.sel_port); +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h b/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h new file mode 100644 index 0000000000..16dbdbec05 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h @@ -0,0 +1,236 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_SVP_PROT_H +#define _LIBVARPD_SVP_PROT_H + +/* + * SVP protocol Definitions + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <sys/ethernet.h> +#include <netinet/in.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SDC VXLAN Protocol Definitions + */ + +#define SVP_VERSION_ONE 1 +#define SVP_CURRENT_VERSION SVP_VERSION_ONE + +typedef struct svp_req { + uint16_t svp_ver; + uint16_t svp_op; + uint32_t svp_size; + uint32_t svp_id; + uint32_t svp_crc32; +} svp_req_t; + +typedef enum svp_op { + SVP_R_UNKNOWN = 0x00, + SVP_R_PING = 0x01, + SVP_R_PONG = 0x02, + SVP_R_VL2_REQ = 0x03, + SVP_R_VL2_ACK = 0x04, + SVP_R_VL3_REQ = 0x05, + SVP_R_VL3_ACK = 0x06, + SVP_R_BULK_REQ = 0x07, + SVP_R_BULK_ACK = 0x08, + SVP_R_LOG_REQ = 0x09, + SVP_R_LOG_ACK = 0x0A, + SVP_R_LOG_RM = 0x0B, + SVP_R_LOG_RM_ACK = 0x0C, + SVP_R_SHOOTDOWN = 0x0D +} svp_op_t; + +typedef enum svp_status { + SVP_S_OK = 0x00, /* Everything OK */ + SVP_S_FATAL = 0x01, /* Fatal error, close connection */ + SVP_S_NOTFOUND = 0x02, /* Entry not found */ + SVP_S_BADL3TYPE = 0x03, /* Unknown svp_vl3_type_t */ + SVP_S_BADBULK = 0x04 /* Unknown svp_bulk_type_t */ +} svp_status_t; + +/* + * A client issues the SVP_R_VL2_REQ whenever it needs to perform a VLS->UL3 + * lookup. Requests have the following structure: + */ +typedef struct svp_vl2_req { + uint8_t sl2r_mac[ETHERADDRL]; + uint8_t sl2r_pad[2]; + uint32_t sl2r_vnetid; +} svp_vl2_req_t; + +/* + * This is the message a server uses to reply to the SVP_R_VL2_REQ. If the + * destination on the underlay is an IPv4 address, it should be encoded as an + * IPv4-mapped IPv6 address. + */ +typedef struct svp_vl2_ack { + uint16_t sl2a_status; + uint16_t sl2a_port; + uint8_t sl2a_addr[16]; +} svp_vl2_ack_t; + + +/* + * A client issues the SVP_R_VL3_REQ request whenever it needs to perform a + * VL3->VL2 lookup. Note, that this also implicitly performs a VL2->UL3 lookup + * as well. The sl3r_type member is used to indicate the kind of lookup type + * that we're performing, eg. is it a L3 or L2. + */ +typedef enum svp_vl3_type { + SVP_VL3_IP = 0x01, + SVP_VL3_IPV6 = 0x02 +} svp_vl3_type_t; + +typedef struct svp_vl3_req { + uint8_t sl3r_ip[16]; + uint32_t sl3r_type; + uint32_t sl3r_vnetid; +} svp_vl3_req_t; + +/* + * This response, corresponding to the SVP_R_VL3_ACK, includes an answer to both + * the VL3->VL2 and the VL2->UL3 requests. + */ +typedef struct svp_vl3_ack { + uint32_t sl3a_status; + uint8_t sl3a_mac[ETHERADDRL]; + uint16_t sl3a_uport; + uint8_t sl3a_uip[16]; +} svp_vl3_ack_t; + +/* + * SVP_R_BULK_REQ requests a bulk dump of data. Currently we have two kinds of + * data tables that we need to dump: VL3->VL2 mappings and VL2->UL3 mappings. + * The kind that we want is indicated using the svbr_type member. + */ +typedef enum svp_bulk_type { + SVP_BULK_VL2 = 0x01, + SVP_BULK_VL3 = 0x02 +} svp_bulk_type_t; + +typedef struct svp_bulk_req { + uint32_t svbr_type; +} svp_bulk_req_t; + +/* + * When replying to a bulk request (SVP_R_BULK_ACK), data is streamed back + * across. The format of the data is currently undefined and as we work on the + * system, we'll get a better understanding of what this should look like. A + * client may need to stream such a request to disk, or the format will need to + * be in a streamable format that allows the client to construct data. + */ +typedef struct svp_bulk_ack { + uint32_t svba_status; + uint32_t svba_type; + uint8_t svba_data[]; +} svp_bulk_ack_t; + +/* + * SVP_R_LOG_REQ requests a log entries from the specified log from the server. + * The total number of bytes that the user is ready to receive is in svlr_count. + * However, the server should not block for data if none is available and thus + * may return less than svlr_count bytes back. We identify the IP address of the + * underlay to use here explicitly. + */ +typedef struct svp_log_req { + uint32_t svlr_count; + uint8_t svlr_ip[16]; +} svp_log_req_t; + +/* + * The server replies to a log request by sending a series of log entries. + * These log entries may be a mixture of both vl2 and vl3 records. The reply is + * a stream of bytes after the status message whose length is determined baseed + * on the header itself. Each entry begins with a uint32_t that describes its + * type and then is followed by the remaining data payload. The next entry + * follows immediately which again begins with the uint32_t word that describes + * what it should be. + */ +typedef enum svp_log_type { + SVP_LOG_VL2 = 0x01, + SVP_LOG_VL3 = 0x02 +} svp_log_type_t; + +typedef struct svp_log_vl2 { + uint32_t svl2_type; /* Should be SVP_LOG_VL2 */ + uint8_t svl2_id[16]; /* 16-byte UUID */ + uint8_t svl2_mac[ETHERADDRL]; + uint8_t svl2_pad[2]; + uint32_t svl2_vnetid; +} svp_log_vl2_t; + +typedef struct svp_log_vl3 { + uint32_t svl3_type; /* Should be SVP_LOG_VL3 */ + uint8_t svl3_id[16]; /* 16-byte UUID */ + uint8_t svl3_ip[16]; + uint8_t svl3_pad[2]; + uint16_t svl3_vlan; + uint32_t svl3_vnetid; +} svp_log_vl3_t; + +typedef struct svp_log_ack { + uint32_t svla_status; + uint8_t svla_data[]; +} svp_log_ack_t; + +/* + * SVP_R_LOG_RM is used after the client successfully processes a series of the + * log stream. It replies to tell the server that it can remove those IDs from + * processing. The IDs used are the same IDs that were in the individual + * SVP_R_LOG_ACK entries. + */ +typedef struct svp_lrm_req { + uint32_t svrr_count; + uint8_t svrr_ids[]; +} svp_lrm_req_t; + +/* + * SVP_R_LOG_RM_ACK is used to indicate that a log entry has been successfully + * deleted and at this point it makes sense to go and ask for another + * SVP_R_LOG_REQ. + */ +typedef struct svp_lrm_ack { + uint32_t svra_status; +} svp_lrm_ack_t; + +/* + * A shootdown (SVP_R_SHOOTDOWN) is used by a CN to reply to another CN that it + * sent an invalid entry that could not be processed. This should be a + * relatively infrequent occurrence. Unlike the rest of the messages, there is + * no reply to it. It's a single request to try and help get us out there. When + * a node receives this, it will issue a conditional revocation ioctl, that + * removes the entry if and only if, it matches the IP. That way if we've + * already gotten an updated entry for this, we don't remove it again. + */ +typedef struct svp_shootdown { + uint8_t svsd_mac[ETHERADDRL]; + uint8_t svsd_pad[2]; + uint32_t svsd_vnetid; +} svp_shootdown_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBVARPD_SVP_PROT_H */ diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c new file mode 100644 index 0000000000..99775f93c0 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c @@ -0,0 +1,821 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * Remote backend management + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + */ + +#include <umem.h> +#include <strings.h> +#include <string.h> +#include <stddef.h> +#include <thread.h> +#include <synch.h> +#include <assert.h> +#include <sys/socket.h> +#include <netdb.h> +#include <errno.h> +#include <libidspace.h> + +#include <libvarpd_provider.h> +#include <libvarpd_svp.h> + +typedef struct svp_shoot_vl3 { + svp_query_t ssv_query; + struct sockaddr_in6 ssv_sock; + svp_log_vl3_t *ssv_vl3; + svp_sdlog_t *ssv_log; +} svp_shoot_vl3_t; + +static mutex_t svp_remote_lock = ERRORCHECKMUTEX; +static avl_tree_t svp_remote_tree; +static svp_timer_t svp_dns_timer; +static id_space_t *svp_idspace; +static int svp_dns_timer_rate = 30; /* seconds */ + +static void +svp_remote_mkfmamsg(svp_remote_t *srp, svp_degrade_state_t state, char *buf, + size_t buflen) +{ + switch (state) { + case SVP_RD_DNS_FAIL: + (void) snprintf(buf, buflen, "failed to resolve or find " + "entries for hostname %s", srp->sr_hostname); + break; + case SVP_RD_REMOTE_FAIL: + (void) snprintf(buf, buflen, "cannot reach any remote peers"); + break; + default: + (void) snprintf(buf, buflen, "unkonwn error state: %d", state); + } +} + +static int +svp_remote_comparator(const void *l, const void *r) +{ + int ret; + const svp_remote_t *lr = l, *rr = r; + + ret = strcmp(lr->sr_hostname, rr->sr_hostname); + if (ret > 0) + return (1); + else if (ret < 0) + return (-1); + + if (lr->sr_rport > rr->sr_rport) + return (1); + else if (lr->sr_rport < rr->sr_rport) + return (-1); + + return (memcmp(&lr->sr_uip, &rr->sr_uip, sizeof (struct in6_addr))); +} + +void +svp_query_release(svp_query_t *sqp) +{ + id_free(svp_idspace, sqp->sq_header.svp_id); +} + +static void +svp_remote_destroy(svp_remote_t *srp) +{ + size_t len; + + /* + * Clean up any unrelated DNS information. At this point we know that + * we're not in the remote tree. That means, that svp_remote_dns_timer + * cannot queue us. However, if any of our DNS related state flags are + * set, we have to hang out. + */ + mutex_enter(&srp->sr_lock); + while (srp->sr_state & + (SVP_RS_LOOKUP_SCHEDULED | SVP_RS_LOOKUP_INPROGRESS)) { + (void) cond_wait(&srp->sr_cond, &srp->sr_lock); + } + mutex_exit(&srp->sr_lock); + svp_shootdown_fini(srp); + + if (cond_destroy(&srp->sr_cond) != 0) + libvarpd_panic("failed to destroy cond sr_cond"); + + if (mutex_destroy(&srp->sr_lock) != 0) + libvarpd_panic("failed to destroy mutex sr_lock"); + + if (srp->sr_addrinfo != NULL) + freeaddrinfo(srp->sr_addrinfo); + len = strlen(srp->sr_hostname) + 1; + umem_free(srp->sr_hostname, len); + umem_free(srp, sizeof (svp_remote_t)); +} + +static int +svp_remote_create(const char *host, uint16_t port, struct in6_addr *uip, + svp_remote_t **outp) +{ + size_t hlen; + svp_remote_t *remote; + + assert(MUTEX_HELD(&svp_remote_lock)); + + remote = umem_zalloc(sizeof (svp_remote_t), UMEM_DEFAULT); + if (remote == NULL) { + mutex_exit(&svp_remote_lock); + return (ENOMEM); + } + + if (svp_shootdown_init(remote) != 0) { + umem_free(remote, sizeof (svp_remote_t)); + mutex_exit(&svp_remote_lock); + return (ENOMEM); + } + + hlen = strlen(host) + 1; + remote->sr_hostname = umem_alloc(hlen, UMEM_DEFAULT); + if (remote->sr_hostname == NULL) { + svp_shootdown_fini(remote); + umem_free(remote, sizeof (svp_remote_t)); + mutex_exit(&svp_remote_lock); + return (ENOMEM); + } + remote->sr_rport = port; + if (mutex_init(&remote->sr_lock, + USYNC_THREAD | LOCK_ERRORCHECK, NULL) != 0) + libvarpd_panic("failed to create mutex sr_lock"); + if (cond_init(&remote->sr_cond, USYNC_PROCESS, NULL) != 0) + libvarpd_panic("failed to create cond sr_cond"); + list_create(&remote->sr_conns, sizeof (svp_conn_t), + offsetof(svp_conn_t, sc_rlist)); + avl_create(&remote->sr_tree, svp_comparator, sizeof (svp_t), + offsetof(svp_t, svp_rlink)); + (void) strlcpy(remote->sr_hostname, host, hlen); + remote->sr_count = 1; + remote->sr_uip = *uip; + + svp_shootdown_start(remote); + + *outp = remote; + return (0); +} + +int +svp_remote_find(char *host, uint16_t port, struct in6_addr *uip, + svp_remote_t **outp) +{ + int ret; + svp_remote_t lookup, *remote; + + lookup.sr_hostname = host; + lookup.sr_rport = port; + lookup.sr_uip = *uip; + mutex_enter(&svp_remote_lock); + remote = avl_find(&svp_remote_tree, &lookup, NULL); + if (remote != NULL) { + assert(remote->sr_count > 0); + remote->sr_count++; + *outp = remote; + mutex_exit(&svp_remote_lock); + return (0); + } + + if ((ret = svp_remote_create(host, port, uip, outp)) != 0) { + mutex_exit(&svp_remote_lock); + return (ret); + } + + avl_add(&svp_remote_tree, *outp); + mutex_exit(&svp_remote_lock); + + /* Make sure DNS is up to date */ + svp_host_queue(*outp); + + return (0); +} + +void +svp_remote_release(svp_remote_t *srp) +{ + mutex_enter(&svp_remote_lock); + mutex_enter(&srp->sr_lock); + srp->sr_count--; + if (srp->sr_count != 0) { + mutex_exit(&srp->sr_lock); + mutex_exit(&svp_remote_lock); + return; + } + mutex_exit(&srp->sr_lock); + + avl_remove(&svp_remote_tree, srp); + mutex_exit(&svp_remote_lock); + svp_remote_destroy(srp); +} + +int +svp_remote_attach(svp_remote_t *srp, svp_t *svp) +{ + svp_t check; + avl_index_t where; + + mutex_enter(&srp->sr_lock); + if (svp->svp_remote != NULL) + libvarpd_panic("failed to create mutex sr_lock"); + + /* + * We require everything except shootdowns + */ + if (svp->svp_cb.scb_vl2_lookup == NULL) + libvarpd_panic("missing callback scb_vl2_lookup"); + if (svp->svp_cb.scb_vl3_lookup == NULL) + libvarpd_panic("missing callback scb_vl3_lookup"); + if (svp->svp_cb.scb_vl2_invalidate == NULL) + libvarpd_panic("missing callback scb_vl2_invalidate"); + if (svp->svp_cb.scb_vl3_inject == NULL) + libvarpd_panic("missing callback scb_vl3_inject"); + + check.svp_vid = svp->svp_vid; + if (avl_find(&srp->sr_tree, &check, &where) != NULL) + libvarpd_panic("found duplicate entry with vid %ld", + svp->svp_vid); + avl_insert(&srp->sr_tree, svp, where); + svp->svp_remote = srp; + mutex_exit(&srp->sr_lock); + + return (0); +} + +void +svp_remote_detach(svp_t *svp) +{ + svp_t *lookup; + svp_remote_t *srp = svp->svp_remote; + + if (srp == NULL) + libvarpd_panic("trying to detach remote when none exists"); + + mutex_enter(&srp->sr_lock); + lookup = avl_find(&srp->sr_tree, svp, NULL); + if (lookup == NULL || lookup != svp) + libvarpd_panic("inconsitent remote avl tree..."); + avl_remove(&srp->sr_tree, svp); + svp->svp_remote = NULL; + mutex_exit(&srp->sr_lock); + svp_remote_release(srp); +} + +/* + * Walk the list of connections and find the first one that's available, the + * move it to the back of the list so it's less likely to be used again. + */ +static boolean_t +svp_remote_conn_queue(svp_remote_t *srp, svp_query_t *sqp) +{ + svp_conn_t *scp; + + assert(MUTEX_HELD(&srp->sr_lock)); + for (scp = list_head(&srp->sr_conns); scp != NULL; + scp = list_next(&srp->sr_conns, scp)) { + mutex_enter(&scp->sc_lock); + if (scp->sc_cstate != SVP_CS_ACTIVE) { + mutex_exit(&scp->sc_lock); + continue; + } + svp_conn_queue(scp, sqp); + mutex_exit(&scp->sc_lock); + list_remove(&srp->sr_conns, scp); + list_insert_tail(&srp->sr_conns, scp); + return (B_TRUE); + } + + return (B_FALSE); +} + +static void +svp_remote_vl2_lookup_cb(svp_query_t *sqp, void *arg) +{ + svp_t *svp = sqp->sq_svp; + svp_vl2_ack_t *vl2a = (svp_vl2_ack_t *)sqp->sq_wdata; + + if (sqp->sq_status == SVP_S_OK) + svp->svp_cb.scb_vl2_lookup(svp, sqp->sq_status, + (struct in6_addr *)vl2a->sl2a_addr, ntohs(vl2a->sl2a_port), + arg); + else + svp->svp_cb.scb_vl2_lookup(svp, sqp->sq_status, NULL, 0, arg); +} + +void +svp_remote_vl2_lookup(svp_t *svp, svp_query_t *sqp, const uint8_t *mac, + void *arg) +{ + svp_remote_t *srp; + svp_vl2_req_t *vl2r = &sqp->sq_rdun.sqd_vl2r; + + srp = svp->svp_remote; + sqp->sq_func = svp_remote_vl2_lookup_cb; + sqp->sq_arg = arg; + sqp->sq_svp = svp; + sqp->sq_state = SVP_QUERY_INIT; + sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION); + sqp->sq_header.svp_op = htons(SVP_R_VL2_REQ); + sqp->sq_header.svp_size = htonl(sizeof (svp_vl2_req_t)); + sqp->sq_header.svp_id = id_alloc(svp_idspace); + if (sqp->sq_header.svp_id == (id_t)-1) + libvarpd_panic("failed to allcoate from svp_idspace: %d", + errno); + sqp->sq_header.svp_crc32 = htonl(0); + sqp->sq_rdata = vl2r; + sqp->sq_rsize = sizeof (svp_vl2_req_t); + sqp->sq_wdata = NULL; + sqp->sq_wsize = 0; + + bcopy(mac, vl2r->sl2r_mac, ETHERADDRL); + vl2r->sl2r_vnetid = ntohl(svp->svp_vid); + + mutex_enter(&srp->sr_lock); + if (svp_remote_conn_queue(srp, sqp) == B_FALSE) + svp->svp_cb.scb_vl2_lookup(svp, SVP_S_FATAL, NULL, 0, arg); + mutex_exit(&srp->sr_lock); +} + +static void +svp_remote_vl3_lookup_cb(svp_query_t *sqp, void *arg) +{ + svp_t *svp = sqp->sq_svp; + svp_vl3_ack_t *vl3a = (svp_vl3_ack_t *)sqp->sq_wdata; + + if (sqp->sq_status == SVP_S_OK) + svp->svp_cb.scb_vl3_lookup(svp, sqp->sq_status, vl3a->sl3a_mac, + (struct in6_addr *)vl3a->sl3a_uip, ntohs(vl3a->sl3a_uport), + arg); + else + svp->svp_cb.scb_vl3_lookup(svp, sqp->sq_status, NULL, NULL, 0, + arg); +} + +static void +svp_remote_vl3_common(svp_remote_t *srp, svp_query_t *sqp, + const struct sockaddr *addr, svp_query_f func, void *arg, uint32_t vid) +{ + svp_vl3_req_t *vl3r = &sqp->sq_rdun.sdq_vl3r; + + if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6) + libvarpd_panic("unexpected sa_family for the vl3 lookup"); + + sqp->sq_func = func; + sqp->sq_arg = arg; + sqp->sq_state = SVP_QUERY_INIT; + sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION); + sqp->sq_header.svp_op = htons(SVP_R_VL3_REQ); + sqp->sq_header.svp_size = htonl(sizeof (svp_vl3_req_t)); + sqp->sq_header.svp_id = id_alloc(svp_idspace); + if (sqp->sq_header.svp_id == (id_t)-1) + libvarpd_panic("failed to allcoate from svp_idspace: %d", + errno); + sqp->sq_header.svp_crc32 = htonl(0); + sqp->sq_rdata = vl3r; + sqp->sq_rsize = sizeof (svp_vl3_req_t); + sqp->sq_wdata = NULL; + sqp->sq_wsize = 0; + + if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)addr; + vl3r->sl3r_type = htonl(SVP_VL3_IPV6); + bcopy(&s6->sin6_addr, vl3r->sl3r_ip, + sizeof (struct in6_addr)); + } else { + struct sockaddr_in *s4 = (struct sockaddr_in *)addr; + struct in6_addr v6; + + vl3r->sl3r_type = htonl(SVP_VL3_IP); + IN6_INADDR_TO_V4MAPPED(&s4->sin_addr, &v6); + bcopy(&v6, vl3r->sl3r_ip, sizeof (struct in6_addr)); + } + vl3r->sl3r_vnetid = htonl(vid); + + mutex_enter(&srp->sr_lock); + if (svp_remote_conn_queue(srp, sqp) == B_FALSE) { + sqp->sq_status = SVP_S_FATAL; + sqp->sq_func(sqp, arg); + } + mutex_exit(&srp->sr_lock); +} + +/* + * This is a request to do a VL3 look-up that originated internally as opposed + * to coming from varpd. As such we need a slightly different query callback + * function upon completion and don't go through the normal path with the svp_t. + */ +void +svp_remote_vl3_logreq(svp_remote_t *srp, svp_query_t *sqp, uint32_t vid, + const struct sockaddr *addr, svp_query_f func, void *arg) +{ + svp_remote_vl3_common(srp, sqp, addr, func, arg, vid); +} + +void +svp_remote_vl3_lookup(svp_t *svp, svp_query_t *sqp, + const struct sockaddr *addr, void *arg) +{ + svp_remote_t *srp = svp->svp_remote; + + sqp->sq_svp = svp; + svp_remote_vl3_common(srp, sqp, addr, svp_remote_vl3_lookup_cb, + arg, svp->svp_vid); +} + +static void +svp_remote_log_request_cb(svp_query_t *sqp, void *arg) +{ + svp_remote_t *srp = sqp->sq_arg; + + assert(sqp->sq_wdata != NULL); + if (sqp->sq_status == SVP_S_OK) + svp_shootdown_logr_cb(srp, sqp->sq_status, sqp->sq_wdata, + sqp->sq_size); + else + svp_shootdown_logr_cb(srp, sqp->sq_status, NULL, 0); +} + +void +svp_remote_log_request(svp_remote_t *srp, svp_query_t *sqp, void *buf, + size_t buflen) +{ + svp_log_req_t *logr = &sqp->sq_rdun.sdq_logr; + boolean_t queued; + + sqp->sq_func = svp_remote_log_request_cb; + sqp->sq_state = SVP_QUERY_INIT; + sqp->sq_arg = srp; + sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION); + sqp->sq_header.svp_op = htons(SVP_R_LOG_REQ); + sqp->sq_header.svp_size = htonl(sizeof (svp_log_req_t)); + sqp->sq_header.svp_id = id_alloc(svp_idspace); + if (sqp->sq_header.svp_id == (id_t)-1) + libvarpd_panic("failed to allcoate from svp_idspace: %d", + errno); + sqp->sq_header.svp_crc32 = htonl(0); + sqp->sq_rdata = logr; + sqp->sq_rsize = sizeof (svp_log_req_t); + sqp->sq_wdata = buf; + sqp->sq_wsize = buflen; + + logr->svlr_count = htonl(buflen); + bcopy(&srp->sr_uip, logr->svlr_ip, sizeof (struct in6_addr)); + + /* + * If this fails, there isn't much that we can't do. Give the callback + * with a fatal status. + */ + mutex_enter(&srp->sr_lock); + queued = svp_remote_conn_queue(srp, sqp); + mutex_exit(&srp->sr_lock); + + if (queued == B_FALSE) + svp_shootdown_logr_cb(srp, SVP_S_FATAL, NULL, 0); +} + +static void +svp_remote_lrm_request_cb(svp_query_t *sqp, void *arg) +{ + svp_remote_t *srp = arg; + + svp_shootdown_lrm_cb(srp, sqp->sq_status); +} + +void +svp_remote_lrm_request(svp_remote_t *srp, svp_query_t *sqp, void *buf, + size_t buflen) +{ + boolean_t queued; + svp_lrm_req_t *svrr = buf; + + sqp->sq_func = svp_remote_lrm_request_cb; + sqp->sq_state = SVP_QUERY_INIT; + sqp->sq_arg = srp; + sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION); + sqp->sq_header.svp_op = htons(SVP_R_LOG_RM); + sqp->sq_header.svp_size = htonl(buflen); + sqp->sq_header.svp_id = id_alloc(svp_idspace); + if (sqp->sq_header.svp_id == (id_t)-1) + libvarpd_panic("failed to allcoate from svp_idspace: %d", + errno); + sqp->sq_header.svp_crc32 = htonl(0); + sqp->sq_rdata = buf; + sqp->sq_rsize = buflen; + sqp->sq_wdata = NULL; + sqp->sq_wsize = 0; + + /* + * We need to fix up the count to be in proper network order. + */ + svrr->svrr_count = htonl(svrr->svrr_count); + + /* + * If this fails, there isn't much that we can't do. Give the callback + * with a fatal status. + */ + mutex_enter(&srp->sr_lock); + queued = svp_remote_conn_queue(srp, sqp); + mutex_exit(&srp->sr_lock); + + if (queued == B_FALSE) + svp_shootdown_logr_cb(srp, SVP_S_FATAL, NULL, 0); +} + +/* ARGSUSED */ +void +svp_remote_dns_timer(void *unused) +{ + svp_remote_t *s; + mutex_enter(&svp_remote_lock); + for (s = avl_first(&svp_remote_tree); s != NULL; + s = AVL_NEXT(&svp_remote_tree, s)) { + svp_host_queue(s); + } + mutex_exit(&svp_remote_lock); +} + +void +svp_remote_resolved(svp_remote_t *srp, struct addrinfo *newaddrs) +{ + struct addrinfo *a; + svp_conn_t *scp; + int ngen; + + mutex_enter(&srp->sr_lock); + srp->sr_gen++; + ngen = srp->sr_gen; + mutex_exit(&srp->sr_lock); + + for (a = newaddrs; a != NULL; a = a->ai_next) { + struct in6_addr in6; + struct in6_addr *addrp; + + if (a->ai_family != AF_INET && a->ai_family != AF_INET6) + continue; + + if (a->ai_family == AF_INET) { + struct sockaddr_in *v4; + v4 = (struct sockaddr_in *)a->ai_addr; + addrp = &in6; + IN6_INADDR_TO_V4MAPPED(&v4->sin_addr, addrp); + } else { + struct sockaddr_in6 *v6; + v6 = (struct sockaddr_in6 *)a->ai_addr; + addrp = &v6->sin6_addr; + } + + mutex_enter(&srp->sr_lock); + for (scp = list_head(&srp->sr_conns); scp != NULL; + scp = list_next(&srp->sr_conns, scp)) { + mutex_enter(&scp->sc_lock); + if (bcmp(addrp, &scp->sc_addr, + sizeof (struct in6_addr)) == 0) { + scp->sc_gen = ngen; + mutex_exit(&scp->sc_lock); + break; + } + mutex_exit(&scp->sc_lock); + } + + /* + * We need to be careful in the assumptions that we make here, + * as there's a good chance that svp_conn_create will + * drop the svp_remote_t`sr_lock to kick off its effective event + * loop. + */ + if (scp == NULL) + (void) svp_conn_create(srp, addrp); + mutex_exit(&srp->sr_lock); + } + + /* + * Now it's time to clean things up. We do not actively clean up the + * current connections that we have, instead allowing them to stay + * around assuming that they're still useful. Instead, we go through and + * purge the degraded list for anything that's from an older generation. + */ + mutex_enter(&srp->sr_lock); + for (scp = list_head(&srp->sr_conns); scp != NULL; + scp = list_next(&srp->sr_conns, scp)) { + boolean_t fall = B_FALSE; + mutex_enter(&scp->sc_lock); + if (scp->sc_gen < srp->sr_gen) + fall = B_TRUE; + mutex_exit(&scp->sc_lock); + if (fall == B_TRUE) + svp_conn_fallout(scp); + } + mutex_exit(&srp->sr_lock); +} + +/* + * This connection is in the process of being reset, we need to reassign all of + * its queries to other places or mark them as fatal. Note that the first + * connection was the one in flight when this failed. We always mark it as + * failed to avoid trying to reset its state. + */ +void +svp_remote_reassign(svp_remote_t *srp, svp_conn_t *scp) +{ + boolean_t first = B_TRUE; + assert(MUTEX_HELD(&srp->sr_lock)); + assert(MUTEX_HELD(&srp->sr_lock)); + svp_query_t *sqp; + + /* + * As we try to reassigning all of its queries, remove it from the list. + */ + list_remove(&srp->sr_conns, scp); + + while ((sqp = list_remove_head(&scp->sc_queries)) != NULL) { + + if (first == B_TRUE) { + sqp->sq_status = SVP_S_FATAL; + sqp->sq_func(sqp, sqp->sq_arg); + continue; + } + + sqp->sq_acttime = -1; + + /* + * We may want to maintain a queue of these for some time rather + * than just failing them all. + */ + if (svp_remote_conn_queue(srp, sqp) == B_FALSE) { + sqp->sq_status = SVP_S_FATAL; + sqp->sq_func(sqp, sqp->sq_arg); + } + } + + /* + * Now that we're done, go ahead and re-insert. + */ + list_insert_tail(&srp->sr_conns, scp); +} + +void +svp_remote_degrade(svp_remote_t *srp, svp_degrade_state_t flag) +{ + int sf, nf; + char buf[256]; + + assert(MUTEX_HELD(&srp->sr_lock)); + + if (flag == SVP_RD_ALL || flag == 0) + libvarpd_panic("invalid flag passed to degrade"); + + if ((flag & srp->sr_degrade) != 0) { + return; + } + + sf = ffs(srp->sr_degrade); + nf = ffs(flag); + srp->sr_degrade |= flag; + if (sf == 0 || sf > nf) { + svp_t *svp; + svp_remote_mkfmamsg(srp, flag, buf, sizeof (buf)); + + for (svp = avl_first(&srp->sr_tree); svp != NULL; + svp = AVL_NEXT(&srp->sr_tree, svp)) { + libvarpd_fma_degrade(svp->svp_hdl, buf); + } + } +} + +void +svp_remote_restore(svp_remote_t *srp, svp_degrade_state_t flag) +{ + int sf, nf; + + assert(MUTEX_HELD(&srp->sr_lock)); + sf = ffs(srp->sr_degrade); + if ((srp->sr_degrade & flag) != flag) + return; + srp->sr_degrade &= ~flag; + nf = ffs(srp->sr_degrade); + + /* + * If we're now empty, restore the device. If we still are degraded, but + * we now have a higher base than we used to, change the message. + */ + if (srp->sr_degrade == 0) { + svp_t *svp; + for (svp = avl_first(&srp->sr_tree); svp != NULL; + svp = AVL_NEXT(&srp->sr_tree, svp)) { + libvarpd_fma_restore(svp->svp_hdl); + } + } else if (nf != sf) { + svp_t *svp; + char buf[256]; + + svp_remote_mkfmamsg(srp, 1U << (nf - 1), buf, sizeof (buf)); + for (svp = avl_first(&srp->sr_tree); svp != NULL; + svp = AVL_NEXT(&srp->sr_tree, svp)) { + libvarpd_fma_degrade(svp->svp_hdl, buf); + } + } +} + +void +svp_remote_shootdown_vl3_cb(svp_query_t *sqp, void *arg) +{ + svp_shoot_vl3_t *squery = arg; + svp_log_vl3_t *svl3 = squery->ssv_vl3; + svp_sdlog_t *sdl = squery->ssv_log; + + if (sqp->sq_status == SVP_S_OK) { + svp_t *svp, lookup; + + svp_remote_t *srp = sdl->sdl_remote; + svp_vl3_ack_t *vl3a = (svp_vl3_ack_t *)sqp->sq_wdata; + + lookup.svp_vid = ntohl(svl3->svl3_vnetid); + mutex_enter(&srp->sr_lock); + if ((svp = avl_find(&srp->sr_tree, &lookup, NULL)) != NULL) { + svp->svp_cb.scb_vl3_inject(svp, ntohs(svl3->svl3_vlan), + (struct in6_addr *)svl3->svl3_ip, vl3a->sl3a_mac, + NULL); + } + mutex_exit(&srp->sr_lock); + + } + + svp_shootdown_vl3_cb(sqp->sq_status, svl3, sdl); + + umem_free(squery, sizeof (svp_shoot_vl3_t)); +} + +void +svp_remote_shootdown_vl3(svp_remote_t *srp, svp_log_vl3_t *svl3, + svp_sdlog_t *sdl) +{ + svp_shoot_vl3_t *squery; + + squery = umem_zalloc(sizeof (svp_shoot_vl3_t), UMEM_DEFAULT); + if (squery == NULL) { + svp_shootdown_vl3_cb(SVP_S_FATAL, svl3, sdl); + return; + } + + squery->ssv_vl3 = svl3; + squery->ssv_log = sdl; + squery->ssv_sock.sin6_family = AF_INET6; + bcopy(svl3->svl3_ip, &squery->ssv_sock.sin6_addr, + sizeof (svl3->svl3_ip)); + svp_remote_vl3_logreq(srp, &squery->ssv_query, ntohl(svl3->svl3_vnetid), + (struct sockaddr *)&squery->ssv_sock, svp_remote_shootdown_vl3_cb, + squery); +} + +void +svp_remote_shootdown_vl2(svp_remote_t *srp, svp_log_vl2_t *svl2) +{ + svp_t *svp, lookup; + + lookup.svp_vid = ntohl(svl2->svl2_vnetid); + mutex_enter(&srp->sr_lock); + if ((svp = avl_find(&srp->sr_tree, &lookup, NULL)) != NULL) { + svp->svp_cb.scb_vl2_invalidate(svp, svl2->svl2_mac); + } + mutex_exit(&srp->sr_lock); +} + +int +svp_remote_init(void) +{ + svp_idspace = id_space_create("svp_req_ids", 1, INT32_MAX); + if (svp_idspace == NULL) + return (errno); + avl_create(&svp_remote_tree, svp_remote_comparator, + sizeof (svp_remote_t), offsetof(svp_remote_t, sr_gnode)); + svp_dns_timer.st_func = svp_remote_dns_timer; + svp_dns_timer.st_arg = NULL; + svp_dns_timer.st_oneshot = B_FALSE; + svp_dns_timer.st_value = svp_dns_timer_rate; + svp_timer_add(&svp_dns_timer); + return (0); +} + +void +svp_remote_fini(void) +{ + svp_timer_remove(&svp_dns_timer); + avl_destroy(&svp_remote_tree); + if (svp_idspace == NULL) + id_space_destroy(svp_idspace); +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c new file mode 100644 index 0000000000..76afb2519f --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c @@ -0,0 +1,474 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Shootdown processing logic. + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + */ + +#include <umem.h> +#include <sys/uuid.h> +#include <assert.h> +#include <strings.h> +#include <errno.h> +#include <sys/debug.h> + +#include <libvarpd_provider.h> +#include <libvarpd_svp.h> + +/* + * When we've determined that there's nothing left for us to do, then we go + * ahead and wait svp_shootdown_base seconds + up to an additional + * svp_shootdown_base seconds before asking again. However, if there is actually + * some work going on, just use the svp_shootdown_cont time. + */ +static int svp_shootdown_base = 5; +static int svp_shootdown_cont = 1; + +/* + * These are sizes for our logack and logrm buffers. The sizing of the shootdown + * buffere would give us approximately 18 or so VL3 entries and 32 VL2 entries + * or some combination thereof. While it's a bit of overkill, we just use the + * same sized buffer for the list of uuids that we pass to remove log entries + * that we've acted upon. + */ +static int svp_shootdown_buf = 1024; + +static void +svp_shootdown_schedule(svp_sdlog_t *sdl, boolean_t cont) +{ + assert(MUTEX_HELD(&sdl->sdl_lock)); + + if (cont == B_TRUE) { + sdl->sdl_timer.st_value = svp_shootdown_cont; + } else { + sdl->sdl_timer.st_value = svp_shootdown_base + + arc4random_uniform(svp_shootdown_base + 1); + } + svp_timer_add(&sdl->sdl_timer); +} + +void +svp_shootdown_lrm_cb(svp_remote_t *srp, svp_status_t status) +{ + svp_sdlog_t *sdl = &srp->sr_shoot; + + mutex_enter(&sdl->sdl_lock); + sdl->sdl_flags &= ~SVP_SD_RUNNING; + svp_shootdown_schedule(sdl, B_TRUE); + mutex_exit(&sdl->sdl_lock); + + if (status != SVP_S_OK) { + (void) bunyan_warn(svp_bunyan, "SVP_R_LOG_RM failed", + BUNYAN_T_STRING, "remote_host", srp->sr_hostname, + BUNYAN_T_INT32, "remote_port", srp->sr_rport, + BUNYAN_T_INT32, "status", status, + BUNYAN_T_END); + } +} + +static void +svp_shootdown_ref(svp_sdlog_t *sdl) +{ + mutex_enter(&sdl->sdl_lock); + sdl->sdl_ref++; + mutex_exit(&sdl->sdl_lock); +} + +static void +svp_shootdown_rele(svp_sdlog_t *sdl) +{ + svp_lrm_req_t *svrr = sdl->sdl_logrm; + boolean_t next; + + mutex_enter(&sdl->sdl_lock); + VERIFY(sdl->sdl_ref > 0); + sdl->sdl_ref--; + if (sdl->sdl_ref > 0) { + mutex_exit(&sdl->sdl_lock); + return; + } + + /* + * At this point we know that we hold the last reference, therefore it's + * safe for us to go ahead and clean up and move on and attempt to + * deliver the reply. We always deliver the reply by going through the + * timer. This can be rather important as the final reference may be + * coming through a failed query and it's not always safe for us to + * callback into the remote routines from this context. + * + * We should only do this if we have a non-zero number of entries to + * take down. + */ + sdl->sdl_flags &= ~SVP_SD_RUNNING; + if (svrr->svrr_count > 0) { + sdl->sdl_flags |= SVP_SD_DORM; + next = B_TRUE; + } else { + next = B_FALSE; + } + svp_shootdown_schedule(sdl, next); + mutex_exit(&sdl->sdl_lock); +} + +/* + * This is a callback used to indicate that the VL3 lookup has completed and an + * entry, if any, has been injected. If the command succeeded, eg. we got that + * the status was OK or that it was not found, then we will add it to he list to + * shoot down. Otherwise, there's nothing else for us to really do here. + */ +void +svp_shootdown_vl3_cb(svp_status_t status, svp_log_vl3_t *vl3, svp_sdlog_t *sdl) +{ + svp_lrm_req_t *svrr = sdl->sdl_logrm; + + mutex_enter(&sdl->sdl_lock); + if (status == SVP_S_OK || status == SVP_S_NOTFOUND) { + bcopy(vl3->svl3_id, &svrr->svrr_ids[svrr->svrr_count * 16], + UUID_LEN); + svrr->svrr_count++; + } + mutex_exit(&sdl->sdl_lock); + + svp_shootdown_rele(sdl); +} + +static int +svp_shootdown_logr_shoot(void *data, svp_log_type_t type, void *arg) +{ + svp_sdlog_t *sdl = arg; + svp_remote_t *srp = sdl->sdl_remote; + svp_lrm_req_t *svrr = sdl->sdl_logrm; + + if (type != SVP_LOG_VL2 && type != SVP_LOG_VL3) + libvarpd_panic("encountered unknown type: %d\n", type); + + if (type == SVP_LOG_VL2) { + svp_log_vl2_t *svl2 = data; + svp_remote_shootdown_vl2(srp, svl2); + mutex_enter(&sdl->sdl_lock); + bcopy(svl2->svl2_id, &svrr->svrr_ids[svrr->svrr_count * 16], + UUID_LEN); + svrr->svrr_count++; + mutex_exit(&sdl->sdl_lock); + } else { + svp_log_vl3_t *svl3 = data; + + /* Take a hold for the duration of this request */ + svp_shootdown_ref(sdl); + svp_remote_shootdown_vl3(srp, svl3, sdl); + } + + return (0); +} + +static int +svp_shootdown_logr_count(void *data, svp_log_type_t type, void *arg) +{ + uint_t *u = arg; + *u = *u + 1; + return (0); +} + + +static int +svp_shootdown_logr_iter(svp_remote_t *srp, void *buf, size_t len, + int (*cb)(void *, svp_log_type_t, void *), void *arg) +{ + int ret; + off_t cboff = 0; + uint32_t *typep, type; + svp_log_vl2_t *svl2; + svp_log_vl3_t *svl3; + + /* Adjust for initial status word */ + assert(len >= sizeof (uint32_t)); + len -= sizeof (uint32_t); + cboff += sizeof (uint32_t); + + while (len > 0) { + size_t opsz; + + if (len < sizeof (uint32_t)) { + (void) bunyan_warn(svp_bunyan, + "failed to get initial shootdown tag", + BUNYAN_T_STRING, "remote_host", srp->sr_hostname, + BUNYAN_T_INT32, "remote_port", srp->sr_rport, + BUNYAN_T_INT32, "response_size", cboff + len, + BUNYAN_T_INT32, "response_offset", cboff, + BUNYAN_T_END); + return (-1); + } + + typep = buf + cboff; + type = ntohl(*typep); + if (type == SVP_LOG_VL2) { + opsz = sizeof (svp_log_vl2_t); + if (len < opsz) { + (void) bunyan_warn(svp_bunyan, + "not enough data for svp_log_vl2_t", + BUNYAN_T_STRING, "remote_host", + srp->sr_hostname, + BUNYAN_T_INT32, "remote_port", + srp->sr_rport, + BUNYAN_T_INT32, "response_size", + cboff + len, + BUNYAN_T_INT32, "response_offset", cboff, + BUNYAN_T_END); + return (-1); + } + svl2 = (void *)typep; + if ((ret = cb(svl2, type, arg)) != 0) + return (ret); + } else if (type == SVP_LOG_VL3) { + + opsz = sizeof (svp_log_vl3_t); + if (len < opsz) { + (void) bunyan_warn(svp_bunyan, + "not enough data for svp_log_vl3_t", + BUNYAN_T_STRING, "remote_host", + srp->sr_hostname, + BUNYAN_T_INT32, "remote_port", + srp->sr_rport, + BUNYAN_T_INT32, "response_size", + cboff + len, + BUNYAN_T_INT32, "response_offset", cboff, + BUNYAN_T_END); + return (-1); + } + svl3 = (void *)typep; + if ((ret = cb(svl3, type, arg)) != 0) + return (ret); + } else { + (void) bunyan_warn(svp_bunyan, + "unknown log structure type", + BUNYAN_T_STRING, "remote_host", + srp->sr_hostname, + BUNYAN_T_INT32, "remote_port", srp->sr_rport, + BUNYAN_T_INT32, "response_size", cboff + len, + BUNYAN_T_INT32, "response_offset", cboff, + BUNYAN_T_INT32, "structure_type", type, + BUNYAN_T_END); + return (-1); + } + len -= opsz; + cboff += opsz; + } + + return (0); +} + +void +svp_shootdown_logr_cb(svp_remote_t *srp, svp_status_t status, void *cbdata, + size_t cbsize) +{ + uint_t count; + svp_sdlog_t *sdl = &srp->sr_shoot; + + if (status != SVP_S_OK) { + (void) bunyan_warn(svp_bunyan, + "log request not OK", + BUNYAN_T_STRING, "remote_host", srp->sr_hostname, + BUNYAN_T_INT32, "remote_port", srp->sr_rport, + BUNYAN_T_INT32, "response_size", cbsize, + BUNYAN_T_INT32, "status", status, + BUNYAN_T_END); + mutex_enter(&sdl->sdl_lock); + sdl->sdl_flags &= ~SVP_SD_RUNNING; + svp_shootdown_schedule(sdl, B_FALSE); + mutex_exit(&sdl->sdl_lock); + return; + } + + /* + * First go ahead and count the number of entries. This effectively + * allows us to validate that all the data is valid, if this fails, then + * we fail the request. + */ + count = 0; + if ((svp_shootdown_logr_iter(srp, cbdata, cbsize, + svp_shootdown_logr_count, &count)) != 0) { + mutex_enter(&sdl->sdl_lock); + sdl->sdl_flags &= ~SVP_SD_RUNNING; + svp_shootdown_schedule(sdl, B_FALSE); + mutex_exit(&sdl->sdl_lock); + return; + } + + /* + * If we have no entries, then we're also done. + */ + if (count == 0) { + mutex_enter(&sdl->sdl_lock); + sdl->sdl_flags &= ~SVP_SD_RUNNING; + svp_shootdown_schedule(sdl, B_FALSE); + mutex_exit(&sdl->sdl_lock); + return; + } + + /* + * We have work to do. Because we may have asynchronous VL3 tasks, we're + * going to first grab a reference before we do the iteration. Then, for + * each asynchronous VL3 request we make, that'll also grab a hold. Once + * we're done with the iteration, we'll drop our hold. If that's the + * last one, it'll move on accordingly. + */ + svp_shootdown_ref(sdl); + bzero(sdl->sdl_logrm, svp_shootdown_buf); + + /* + * If this fails, we're going to determine what to do next based on the + * number of entries that were entered into the log removal. At this + * point success or failure don't really look different, all it changes + * is how many entries we have to remove. + */ + (void) svp_shootdown_logr_iter(srp, cbdata, cbsize, + svp_shootdown_logr_shoot, sdl); + + /* + * Now that we're done with our work, release the hold. If we don't have + * any vl3 tasks outstanding, this'll trigger the next phase of the log + * removals. + */ + svp_shootdown_rele(sdl); +} + +static void +svp_shootdown_timer(void *arg) +{ + svp_sdlog_t *sdl = arg; + svp_remote_t *srp = sdl->sdl_remote; + boolean_t init = B_TRUE; + + mutex_enter(&sdl->sdl_lock); + + /* + * If we've been asked to quiesce, we're done. + */ + if ((sdl->sdl_flags & SVP_SD_QUIESCE) != 0) { + mutex_exit(&sdl->sdl_lock); + return; + } + + /* + * We shouldn't be able to have ourselves currently be running and reach + * here. If that's the case, we should immediately panic. + */ + if ((sdl->sdl_flags & SVP_SD_RUNNING) != 0) { + libvarpd_panic("remote %p shootdown timer fired while still " + "running", srp); + } + + if ((sdl->sdl_flags & SVP_SD_DORM) != 0) { + sdl->sdl_flags &= ~SVP_SD_DORM; + init = B_FALSE; + } + + sdl->sdl_flags |= SVP_SD_RUNNING; + mutex_exit(&sdl->sdl_lock); + + if (init == B_FALSE) { + svp_lrm_req_t *svrr = sdl->sdl_logrm; + + bzero(&sdl->sdl_query, sizeof (svp_query_t)); + svp_remote_lrm_request(sdl->sdl_remote, &sdl->sdl_query, svrr, + sizeof (*svrr) + 16 * svrr->svrr_count); + } else { + bzero(&sdl->sdl_query, sizeof (svp_query_t)); + svp_remote_log_request(srp, &sdl->sdl_query, sdl->sdl_logack, + svp_shootdown_buf); + } +} + +void +svp_shootdown_fini(svp_remote_t *srp) +{ + svp_sdlog_t *sdl = &srp->sr_shoot; + + mutex_enter(&sdl->sdl_lock); + sdl->sdl_flags |= SVP_SD_QUIESCE; + mutex_exit(&sdl->sdl_lock); + + svp_timer_remove(&sdl->sdl_timer); + + mutex_enter(&sdl->sdl_lock); + + /* + * Normally svp_timer_remove would be enough. However, the query could + * have been put out again outside of the svp_timer interface. Therefore + * we still need to check for SVP_SD_RUNNING. + */ + while (sdl->sdl_flags & SVP_SD_RUNNING) + (void) cond_wait(&sdl->sdl_cond, &sdl->sdl_lock); + mutex_exit(&sdl->sdl_lock); + + umem_free(sdl->sdl_logack, svp_shootdown_buf); + umem_free(sdl->sdl_logrm, svp_shootdown_buf); + sdl->sdl_logack = NULL; + sdl->sdl_logrm = NULL; + (void) cond_destroy(&sdl->sdl_cond); + (void) mutex_destroy(&sdl->sdl_lock); +} + +void +svp_shootdown_start(svp_remote_t *srp) +{ + svp_sdlog_t *sdl = &srp->sr_shoot; + + mutex_enter(&sdl->sdl_lock); + svp_shootdown_schedule(sdl, B_FALSE); + mutex_exit(&sdl->sdl_lock); +} + +int +svp_shootdown_init(svp_remote_t *srp) +{ + int ret; + svp_sdlog_t *sdl = &srp->sr_shoot; + if ((ret = mutex_init(&sdl->sdl_lock, USYNC_THREAD | LOCK_ERRORCHECK, + NULL)) != 0) + return (ret); + + if ((ret = cond_init(&sdl->sdl_cond, USYNC_THREAD, NULL)) != 0) { + (void) mutex_destroy(&sdl->sdl_lock); + return (ret); + } + + if ((sdl->sdl_logack = umem_alloc(svp_shootdown_buf, UMEM_DEFAULT)) == + NULL) { + ret = errno; + (void) cond_destroy(&sdl->sdl_cond); + (void) mutex_destroy(&sdl->sdl_lock); + return (ret); + } + + if ((sdl->sdl_logrm = umem_alloc(svp_shootdown_buf, UMEM_DEFAULT)) == + NULL) { + ret = errno; + umem_free(sdl->sdl_logack, svp_shootdown_buf); + (void) cond_destroy(&sdl->sdl_cond); + (void) mutex_destroy(&sdl->sdl_lock); + return (ret); + } + + sdl->sdl_remote = srp; + sdl->sdl_timer.st_oneshot = B_TRUE; + sdl->sdl_timer.st_func = svp_shootdown_timer; + sdl->sdl_timer.st_arg = sdl; + + return (0); +} diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c new file mode 100644 index 0000000000..10b02748f3 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c @@ -0,0 +1,150 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + +#include <stddef.h> +#include <libvarpd_svp.h> + +/* + * svp timer backend + * + * This implements all of the logic of maintaining a timer for the svp backend. + * We have a timer that fires at a one second tick. We maintain all of our + * events in avl tree, sorted by the tick that they need to be processed at. + * + * For more information, see the big theory statement in + * lib/varpd/svp/common/libvarpd_svp.c. + */ + +int svp_tickrate = 1; +static svp_event_t svp_timer_event; +static mutex_t svp_timer_lock = ERRORCHECKMUTEX; +static cond_t svp_timer_cv = DEFAULTCV; +static avl_tree_t svp_timer_tree; +static uint64_t svp_timer_nticks; + +static int +svp_timer_comparator(const void *l, const void *r) +{ + const svp_timer_t *lt, *rt; + + lt = l; + rt = r; + + if (lt->st_expire > rt->st_expire) + return (1); + else if (lt->st_expire < rt->st_expire) + return (-1); + + /* + * Multiple timers can have the same delivery time, so sort within that + * by the address of the timer itself. + */ + if ((uintptr_t)lt > (uintptr_t)rt) + return (1); + else if ((uintptr_t)lt < (uintptr_t)rt) + return (-1); + + return (0); +} + +/* ARGSUSED */ +static void +svp_timer_tick(port_event_t *pe, void *arg) +{ + mutex_enter(&svp_timer_lock); + svp_timer_nticks++; + + for (;;) { + svp_timer_t *t; + + t = avl_first(&svp_timer_tree); + if (t == NULL || t->st_expire > svp_timer_nticks) + break; + + avl_remove(&svp_timer_tree, t); + + /* + * We drop this while performing an operation so that way state + * can advance in the face of a long-running callback. + */ + t->st_delivering = B_TRUE; + mutex_exit(&svp_timer_lock); + t->st_func(t->st_arg); + mutex_enter(&svp_timer_lock); + t->st_delivering = B_FALSE; + (void) cond_broadcast(&svp_timer_cv); + if (t->st_oneshot == B_FALSE) { + t->st_expire += t->st_value; + avl_add(&svp_timer_tree, t); + } + } + mutex_exit(&svp_timer_lock); +} + +void +svp_timer_add(svp_timer_t *stp) +{ + if (stp->st_value == 0) + libvarpd_panic("tried to add svp timer with zero value"); + + mutex_enter(&svp_timer_lock); + stp->st_delivering = B_FALSE; + stp->st_expire = svp_timer_nticks + stp->st_value; + avl_add(&svp_timer_tree, stp); + mutex_exit(&svp_timer_lock); +} + +void +svp_timer_remove(svp_timer_t *stp) +{ + mutex_enter(&svp_timer_lock); + + /* + * If the event in question is not currently being delivered, then we + * can stop it before it next fires. If it is currently being delivered, + * we need to wait for that to finish. Because we hold the timer lock, + * we know that it cannot be rearmed. Therefore, we make sure the one + * shot is set to zero, and wait until it's no longer set to delivering. + */ + if (stp->st_delivering == B_FALSE) { + avl_remove(&svp_timer_tree, stp); + mutex_exit(&svp_timer_lock); + return; + } + + stp->st_oneshot = B_TRUE; + while (stp->st_delivering == B_TRUE) + (void) cond_wait(&svp_timer_cv, &svp_timer_lock); + + mutex_exit(&svp_timer_lock); +} + +int +svp_timer_init(void) +{ + int ret; + + svp_timer_event.se_func = svp_timer_tick; + svp_timer_event.se_arg = NULL; + + avl_create(&svp_timer_tree, svp_timer_comparator, sizeof (svp_timer_t), + offsetof(svp_timer_t, st_link)); + + if ((ret = svp_event_timer_init(&svp_timer_event)) != 0) { + avl_destroy(&svp_timer_tree); + } + + return (ret); +} diff --git a/usr/src/lib/varpd/svp/common/llib-lvarpd_svp b/usr/src/lib/varpd/svp/common/llib-lvarpd_svp new file mode 100644 index 0000000000..03c34f4fcb --- /dev/null +++ b/usr/src/lib/varpd/svp/common/llib-lvarpd_svp @@ -0,0 +1,18 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + diff --git a/usr/src/lib/varpd/svp/common/mapfile-vers b/usr/src/lib/varpd/svp/common/mapfile-vers new file mode 100644 index 0000000000..6b7c5a5067 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/mapfile-vers @@ -0,0 +1,35 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate { + local: + *; +}; diff --git a/usr/src/lib/varpd/svp/i386/Makefile b/usr/src/lib/varpd/svp/i386/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/svp/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/svp/sparc/Makefile b/usr/src/lib/varpd/svp/sparc/Makefile new file mode 100644 index 0000000000..f2b4f63da5 --- /dev/null +++ b/usr/src/lib/varpd/svp/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/varpd/svp/sparcv9/Makefile b/usr/src/lib/varpd/svp/sparcv9/Makefile new file mode 100644 index 0000000000..d552642882 --- /dev/null +++ b/usr/src/lib/varpd/svp/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/xml/os_dtd.c b/usr/src/lib/xml/os_dtd.c new file mode 100644 index 0000000000..579e99ba3c --- /dev/null +++ b/usr/src/lib/xml/os_dtd.c @@ -0,0 +1,238 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Utility functions for working with XML documents that are validated against + * Document Type Definitions (DTD) shipped in the operating system. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <zone.h> + +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#include "os_dtd.h" + +struct os_dtd_path { + os_dtd_id_t odp_id; + const char *odp_name; + const char *odp_public_ident; + const char *odp_path; +}; + +static os_dtd_path_t os_dtd_paths[] = { + /* + * DTDs for Zones infrastructure: + */ + { OS_DTD_ZONES_BRAND, "brand", + "-//Sun Microsystems Inc//DTD Brands//EN", + "/usr/share/lib/xml/dtd/brand.dtd.1" }, + { OS_DTD_ZONES_ZONE, "zone", + "-//Sun Microsystems Inc//DTD Zones//EN", + "/usr/share/lib/xml/dtd/zonecfg.dtd.1" }, + { OS_DTD_ZONES_PLATFORM, "platform", + "-//Sun Microsystems Inc//Zones Platform//EN", + "/usr/share/lib/xml/dtd/zone_platform.dtd.1" }, + + /* + * DTDs for smf(5): + */ + { OS_DTD_SMF_SERVICE_BUNDLE, "service_bundle", + NULL, + "/usr/share/lib/xml/dtd/service_bundle.dtd.1" }, + + { OS_DTD_UNKNOWN, NULL, NULL, NULL } +}; + +/* + * Check this document to see if it references the public identifier of a + * well-known DTD that we ship with the operating system. If there is no DTD, + * or the public identifier is unknown to us, return OS_DTD_UNKNOWN. + */ +os_dtd_id_t +os_dtd_identify(xmlDocPtr doc) +{ + xmlDtdPtr dp; + int i; + + if ((dp = xmlGetIntSubset(doc)) == NULL) { + /* + * This document does not have an internal subset pointing + * to a DTD. + */ + errno = EIO; + return (OS_DTD_UNKNOWN); + } + + /* + * The use of a symbolic name via the public identifier is preferred. + * Check to see if the document refers to a public identifier for any + * well-known DTD: + */ + for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) { + os_dtd_path_t *odp = &os_dtd_paths[i]; + const xmlChar *pubid = (const xmlChar *)odp->odp_public_ident; + + if (dp->ExternalID == NULL || odp->odp_public_ident == NULL) { + continue; + } + + if (xmlStrEqual(pubid, dp->ExternalID)) { + return (odp->odp_id); + } + } + + /* + * If a public identifier is not present, or does not match any known + * DTD, fall back to inspecting the system identifier. + */ + for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) { + os_dtd_path_t *odp = &os_dtd_paths[i]; + char uri[sizeof ("file://") + MAXPATHLEN]; + const xmlChar *path = (const xmlChar *)odp->odp_path; + + if (dp->SystemID == NULL || odp->odp_path == NULL) { + continue; + } + + /* + * The system identifier may be a regular path. + */ + if (xmlStrEqual(path, dp->SystemID)) { + return (odp->odp_id); + } + + /* + * The system identifier may also be a "file://" + * URI referring to a path: + */ + (void) snprintf(uri, sizeof (uri), "file://%s", odp->odp_path); + if (xmlStrEqual((const xmlChar *)uri, dp->SystemID)) { + return (odp->odp_id); + } + } + + errno = ENOENT; + return (OS_DTD_UNKNOWN); +} + +static os_dtd_path_t * +os_dtd_lookup(os_dtd_id_t id) +{ + int i; + + for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) { + os_dtd_path_t *odp = &os_dtd_paths[i]; + + if (odp->odp_id == id) { + return (odp); + } + } + + errno = ENOENT; + return (NULL); +} + +/* + * If this document references a DTD, remove that reference (the "internal + * subset"). Install a new internal subset reference to the well-known + * operating system DTD passed by the caller. The URI in this reference will + * respect the current native system prefix (e.g. "/native") if there is one, + * such as when running in a branded zone. + */ +int +os_dtd_attach(xmlDocPtr doc, os_dtd_id_t id) +{ + xmlDtdPtr dp; + os_dtd_path_t *odp; + const char *zroot = zone_get_nroot(); + char uri[sizeof ("file://") + MAXPATHLEN]; + + if ((odp = os_dtd_lookup(id)) == NULL) { + return (-1); + } + + if ((dp = xmlGetIntSubset(doc)) != NULL) { + /* + * This document already has an internal subset. Remove it + * before attaching the new one. + */ + xmlUnlinkNode((xmlNodePtr)dp); + xmlFreeNode((xmlNodePtr)dp); + } + + /* + * The "system identifier" of this internal subset must refer to the + * path in the filesystem where the DTD file (the external subset) is + * stored. If we are running in a branded zone, that file may be at a + * different path (e.g. under "/native"). + */ + (void) snprintf(uri, sizeof (uri), "file://%s%s", zroot != NULL ? + zroot : "", odp->odp_path); + + if (xmlCreateIntSubset(doc, (const xmlChar *)odp->odp_name, + (const xmlChar *)odp->odp_public_ident, + (const xmlChar *)uri) == NULL) { + errno = EIO; + return (-1); + } + + return (0); +} + +/*ARGSUSED*/ +static void +os_dtd_print_nothing(void *ctx, const char *msg, ...) +{ +} + +int +os_dtd_validate(xmlDocPtr doc, boolean_t emit_messages, boolean_t *valid) +{ + int ret; + xmlValidCtxtPtr cvp; + os_dtd_id_t dtdid; + + if ((dtdid = os_dtd_identify(doc)) != OS_DTD_UNKNOWN) { + /* + * This document refers to a well-known DTD shipped with + * the operating system. Ensure that it points to the + * correct local path for validation in the current context. + */ + if (os_dtd_attach(doc, dtdid) != 0) { + return (-1); + } + } + + if ((cvp = xmlNewValidCtxt()) == NULL) { + return (-1); + } + + if (!emit_messages) { + cvp->error = os_dtd_print_nothing; + cvp->warning = os_dtd_print_nothing; + } + + ret = xmlValidateDocument(cvp, doc); + xmlFreeValidCtxt(cvp); + + *valid = (ret == 1 ? B_TRUE : B_FALSE); + return (0); +} diff --git a/usr/src/lib/xml/os_dtd.h b/usr/src/lib/xml/os_dtd.h new file mode 100644 index 0000000000..c6fe24a293 --- /dev/null +++ b/usr/src/lib/xml/os_dtd.h @@ -0,0 +1,49 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _OS_DTD_H +#define _OS_DTD_H + +/* + * Utility functions for working with XML documents that are validated against + * Document Type Definitions (DTD) shipped in the operating system. + */ + +#include <libxml/parser.h> +#include <libxml/xmlmemory.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum os_dtd_id { + OS_DTD_UNKNOWN = 0, + OS_DTD_ZONES_BRAND, + OS_DTD_ZONES_ZONE, + OS_DTD_ZONES_PLATFORM, + OS_DTD_SMF_SERVICE_BUNDLE +} os_dtd_id_t; + +typedef struct os_dtd_path os_dtd_path_t; + +extern os_dtd_id_t os_dtd_identify(xmlDocPtr); +extern int os_dtd_attach(xmlDocPtr, os_dtd_id_t); +extern int os_dtd_validate(xmlDocPtr, boolean_t, boolean_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _OS_DTD_H */ |