diff options
Diffstat (limited to 'usr/src/lib')
576 files changed, 100430 insertions, 5469 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 6d8588d839..35c5c53004 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -22,7 +22,7 @@ # Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2012 by Delphix. All rights reserved. -# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# Copyright 2015, Joyent, Inc. # Copyright (c) 2013 Gary Mills # Copyright 2014 Garrett D'Amore <garrett@damore.org> @@ -68,6 +68,7 @@ SUBDIRS += \ libcommputil \ libresolv \ libresolv2 .WAIT \ + libresolv2_joy .WAIT \ libw .WAIT \ libintl .WAIT \ ../cmd/sgs/librtld_db \ @@ -80,6 +81,8 @@ SUBDIRS += \ librt \ libadm \ libctf \ + libbrand .WAIT \ + libzonecfg .WAIT \ libdtrace \ libdtrace_jni \ libcurses \ @@ -134,6 +137,7 @@ SUBDIRS += \ libpp \ libproc \ libproject \ + librename \ libsendfile \ nametoaddr \ ncad_addr \ @@ -158,8 +162,11 @@ SUBDIRS += \ libpcidb \ libm1 \ libm \ - libmvec - + libmvec \ + libvnd \ + libidspace \ + varpd \ + libbunyan SUBDIRS += \ passwdutil \ @@ -182,7 +189,6 @@ SUBDIRS += \ libxnet \ libipsecutil \ nsswitch \ - print \ libuutil \ libscf \ libinetsvc \ @@ -201,10 +207,10 @@ SUBDIRS += \ libwrap \ libxcurses \ libxcurses2 \ - libbrand .WAIT \ - libzonecfg \ + libzdoor \ libzoneinfo \ libzonestat \ + libsmartsshd \ libtsnet \ libtsol \ gss_mechs/mech_spnego \ @@ -219,7 +225,6 @@ SUBDIRS += \ raidcfg_plugins \ cfgadm_plugins \ libmail \ - lvm \ libsmedia \ libipp \ libdiskmgt \ @@ -236,10 +241,7 @@ SUBDIRS += \ libzfs_core \ libzfs \ libbe \ - pylibbe \ libzfs_jni \ - pyzfs \ - pysolaris \ libmapid \ brand \ policykit \ @@ -298,9 +300,6 @@ fm: \ # NOWAIT_SUBDIRS= $(SUBDIRS:.WAIT=) -DCSUBDIRS = \ - lvm - MSGSUBDIRS= \ abi \ auditd_plugins \ @@ -355,12 +354,11 @@ MSGSUBDIRS= \ libwanbootutil \ libzfs \ libzonecfg \ - lvm \ + libzdoor \ + libsmartsshd \ madv \ mpss \ pam_modules \ - pyzfs \ - pysolaris \ rpcsec_gss \ libreparse MSGSUBDIRS += \ @@ -377,6 +375,7 @@ HDRSUBDIRS= \ libast \ libbrand \ libbsm \ + libbunyan \ libc \ libcmd \ libcmdutils \ @@ -406,6 +405,7 @@ HDRSUBDIRS= \ libfru \ libfstyp \ libgen \ + libidspace \ libipadm \ libipd \ libipsecutil \ @@ -437,6 +437,7 @@ HDRSUBDIRS= \ libraidcfg \ librcm \ librdc \ + librename \ libscf \ libsip \ libsmbios \ @@ -468,6 +469,7 @@ HDRSUBDIRS= \ libumem \ libunistat \ libuutil \ + libvnd \ libwanboot \ libwanbootutil \ libwrap \ @@ -479,7 +481,6 @@ HDRSUBDIRS= \ libzonestat \ hal \ policykit \ - lvm \ pkcs11 \ passwdutil \ ../cmd/sendmail/libmilter \ @@ -563,7 +564,7 @@ gss_mechs/mech_krb5: libgss libnsl libsocket libresolv pkcs11 libadt_jni: libbsm libast: libsocket libm libadutils: libldap5 libresolv libsocket libnsl -nsswitch: libadutils libidmap +nsswitch: libadutils libidmap libresolv2_joy libbe: libzfs libbsm: libtsol libcmd: libsum libast libsocket libnsl @@ -574,7 +575,7 @@ libdevinfo: libnvpair libsec libdhcpagent: libsocket libdhcputil libuuid libdlpi libcontract libdhcputil: libnsl libgen libinetutil libdlpi libdladm: libdevinfo libinetutil libsocket libscf librcm libnvpair \ - libexacct libnsl libkstat libcurses + libexacct libnsl libkstat libcurses varpd libdll: libast libdlpi: libinetutil libdladm libds: libsysevent @@ -618,6 +619,8 @@ libpool: libnvpair libexacct libpp: libast libzonecfg: libc libsocket libnsl libuuid libnvpair libsysevent libsec \ libbrand libpool libscf +libzdoor: libc libzonecfg libcontract +libsmartsshd: libc libcontract libproc: ../cmd/sgs/librtld_db ../cmd/sgs/libelf libctf libsaveargs libproject: libpool libproc libsecdb libtermcap: libcurses @@ -633,8 +636,6 @@ librestart: libuutil libscf libsaveargs: libdisasm ../cmd/sgs/libdl: ../cmd/sgs/libconv ../cmd/sgs/libelf: ../cmd/sgs/libconv -pkcs11: libcryptoutil -print: libldap5 udapl/udapl_tavor: udapl/libdat libzfs: libdevid libgen libnvpair libuutil \ libadm libavl libefi libidmap libmd libzfs_core libm @@ -659,9 +660,6 @@ sun_fc: libdevinfo libsysevent libnvpair libsun_ima: libdevinfo libsysevent libnsl sun_sas: libdevinfo libsysevent libnvpair libkstat libdevid libgrubmgmt: libdevinfo libzfs libfstyp -pylibbe: libbe libzfs -pyzfs: libnvpair libzfs -pysolaris: libsec libidmap libreparse: libnvpair libhotplug: libnvpair cfgadm_plugins: libhotplug @@ -671,9 +669,13 @@ libprtdiag: libm libsqlite: libm libstmf: libm libvscan: libm +libidspace: libumem +libbunyan: libnvpair $(INTEL_BUILD)libdiskmgt:libfdisk +varpd: libavl libidspace libumem libnsl libnvpair libmd5 librename \ + libbunyan libcmdutils # # 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 5574164fb5..a1078ffc89 100644 --- a/usr/src/lib/Makefile.lib +++ b/usr/src/lib/Makefile.lib @@ -20,6 +20,7 @@ # # Copyright 2015 Gary Mills # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, Joyent, Inc. # # # Definitions common to libraries. @@ -135,6 +136,23 @@ SONAME= $(DYNLIB) # 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) \ @@ -157,7 +175,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) BUILDCCC.SO= $(CCC) $(CCFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \ $(PICS) $(EXTPICS) $(LDLIBS) @@ -254,3 +272,15 @@ TARGETMACH= $(MACH) # shouldn't override this - they should override $(CLOBBERFILES) instead. # CLOBBERTARGFILES= $(LIBS) $(DYNLIB) $(CLOBBERFILES) + +# +# Define the default ctfdiff invocation used to check a list of types +# supplied by a user of a library. The goal is to validate that a given +# series of types is the same in both a 32-bit and 64-bit artifact. This +# is only defined if we have a 64-bit build to do. +# +TYPECHECK_LIB32 = $(TYPECHECK_LIB:%=$(MACH)/%) +TYPECHECK_LIB64 = $(TYPECHECK_LIB:%=$(MACH64)/%) +TYPECHECK_LIST= $(TYPELIST:%=-T %) +$(BUILD64)TYPECHECK.lib = $(CTFDIFF) -t -I $(TYPECHECK_LIST) $(TYPECHECK_LIB32) $(TYPECHECK_LIB64) +TYPECHECK = $(TYPECHECK_LIB:%=%.typecheck) diff --git a/usr/src/lib/Makefile.targ b/usr/src/lib/Makefile.targ index 4769c64d54..2798192343 100644 --- a/usr/src/lib/Makefile.targ +++ b/usr/src/lib/Makefile.targ @@ -20,6 +20,7 @@ # # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, Joyent, Inc. # # @@ -90,7 +91,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) @@ -104,9 +105,12 @@ $(LINTLIB): $$(SRCS) lintcheck: $$(SRCS) $(LINT.c) $(LINTCHECKFLAGS) $(SRCS) $(LDLIBS) +$(TYPECHECK): $(TYPECHECK_LIB32) $(TYPECHECK_LIB64) + $(TYPECHECK.lib) + clobber: clean -$(RM) $(CLOBBERTARGFILES) clean: - -$(RM) $(OBJS) + -$(RM) $(OBJS) $(USDT_HEADERS) $(USDT_PICS) -$(RM) $(PICS) $(DUPLICATE_SRC) $(LINTOUT) $(LINTLIB) $(CLEANFILES) 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 05bfc54764..bd766f3f9d 100644 --- a/usr/src/lib/brand/Makefile +++ b/usr/src/lib/brand/Makefile @@ -30,7 +30,10 @@ include ../../Makefile.master # Build everything in parallel; use .WAIT for dependencies .PARALLEL: -SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled $($(MACH)_SUBDIRS) +i386_SUBDIRS= lx +i386_MSGSUBDIRS= lx + +SUBDIRS= shared .WAIT sn1 sngl solaris10 ipkg labeled $($(MACH)_SUBDIRS) MSGSUBDIRS= solaris10 shared $($(MACH)_MSGSUBDIRS) all := TARGET= all diff --git a/usr/src/lib/brand/lx/Makefile b/usr/src/lib/brand/lx/Makefile new file mode 100644 index 0000000000..2c5a373e25 --- /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 2015 Joyent, Inc. +# + +default: all + +include Makefile.lx + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS= cmd librtld_db lx_support lx_init 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/cmd/Makefile b/usr/src/lib/brand/lx/cmd/Makefile new file mode 100644 index 0000000000..3d9c4009b3 --- /dev/null +++ b/usr/src/lib/brand/lx/cmd/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. +# + +PROGS = lx_native + +include ../Makefile.lx + +# override the install directory +ROOTBIN = $(ROOTBRANDDIR) +CLOBBERFILES = $(ROOTPROGS) + +.KEEP_STATE: + +lint: + +all: $(PROGS) + +install: all $(ROOTPROGS) + +clean: + $(RM) $(PROGS) + +clobber: clean + $(RM) $(ROOTPROGS) diff --git a/usr/src/lib/brand/lx/cmd/lx_native.sh b/usr/src/lib/brand/lx/cmd/lx_native.sh new file mode 100644 index 0000000000..8e8344a375 --- /dev/null +++ b/usr/src/lib/brand/lx/cmd/lx_native.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# 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" +# +exit 0 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..202cc0fe7b --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/Makefile.com @@ -0,0 +1,83 @@ +# +# 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" +# + +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. +# +DYNFLAGS += $(VERSREF) -M../common/mapfile-vers +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) + +ROOTLIBDIR = $(ROOT)/usr/lib/brand/lx +ROOTLIBDIR64 = $(ROOT)/usr/lib/brand/lx/$(MACH64) + +# +# The top level Makefiles define 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) + +lint: lintcheck + +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..726e7ef6d3 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/amd64/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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +COBJS64 = lx_librtld_db64.o + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +DYNFLAGS += -Mmapfile-vers + +CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB) + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/brand/lx/librtld_db/amd64/mapfile-vers b/usr/src/lib/brand/lx/librtld_db/amd64/mapfile-vers new file mode 100644 index 0000000000..4893b02998 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/amd64/mapfile-vers @@ -0,0 +1,44 @@ +# +# 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. +# + +# +# 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 +# + +SUNWprivate_1.1 { + global: + rtld_db_brand_ops64; +}; 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..50cf3ca3ba --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c @@ -0,0 +1,851 @@ +/* + * 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 2014 Joyent, Inc. All rights reserved. + */ + +#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 +#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 = NULL; + + 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 = 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 != 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 = 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/common/mapfile-vers b/usr/src/lib/brand/lx/librtld_db/common/mapfile-vers new file mode 100644 index 0000000000..5e328d6075 --- /dev/null +++ b/usr/src/lib/brand/lx/librtld_db/common/mapfile-vers @@ -0,0 +1,58 @@ +# +# 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. +# + +# +# 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 +# + +{ + global: + rtld_db_brand_ops32; + local: + *; +}; + +#Externally defined symbols +{ + global: + ps_pauxv = NODIRECT PARENT; + ps_pdmodel = NODIRECT PARENT; + ps_pglobal_lookup = NODIRECT PARENT; + ps_pglobal_sym = NODIRECT PARENT; + ps_plog = NODIRECT PARENT; + ps_pread = NODIRECT PARENT; + ps_pwrite = NODIRECT PARENT; +}; 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..b75657dbe9 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/Makefile.com @@ -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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2015 Joyent, Inc. +# + +LX_CMN = $(SRC)/common/brand/lx + +LIBRARY = lx_brand.a +VERS = .1 +COBJS = aio.o \ + capabilities.o \ + clock.o \ + clone.o \ + debug.o \ + dir.o \ + file.o \ + fcntl.o \ + fork.o \ + id.o \ + iovec.o \ + lx_brand.o \ + mem.o \ + misc.o \ + module.o \ + mount.o \ + mount_nfs.o \ + pgrp.o \ + poll_select.o \ + priority.o \ + ptrace.o \ + sched.o \ + sendfile.o \ + signal.o \ + socket.o \ + stack.o \ + stat.o \ + statfs.o \ + sysctl.o \ + sysv_ipc.o \ + time.o \ + truncate.o + +CMNOBJS = 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 += -lc -lsocket -lmapmalloc -lproc -lrtld_db -lrpcsvc -lnsl +DYNFLAGS += $(DYNFLAGS_$(CLASS)) +DYNFLAGS += $(BLOCAL) $(ZNOVERSION) -Wl,-e_start -M../common/mapfile +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 + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../../../Makefile.targ +include ../../../../Makefile.usdt + +pics/%.o: $(ISASRCDIR)/%.s + $(COMPILE.s) -o $@ $< + $(POST_PROCESS_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..664cb8de56 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile @@ -0,0 +1,51 @@ +# +# 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. +# +# +# 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 + +$(ASSYMDEP_OBJS:%=pics/%): assym.h + +OFFSETS = ../$(MACH64)/offsets.in + +assym.h: $(OFFSETS) + $(OFFSETS_CREATE) $(CTF_FLAGS) < $(OFFSETS) > $@ + +CLOBBERFILES += assym.h + +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..33978c08dd --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s @@ -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 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 */ + +#include "assym.h" + +/* 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/amd64/offsets.in b/usr/src/lib/brand/lx/lx_brand/amd64/offsets.in new file mode 100644 index 0000000000..207063a886 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/amd64/offsets.in @@ -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 2014 Joyent, Inc. All rights reserved. +\ + +#include <sys/lx_brand.h> +#include <sys/lx_sigstack.h> + +lx_regs_t SIZEOF_LX_REGS_T + lxr_fs + lxr_rdi + lxr_rsi + lxr_rbp + lxr_rsp + lxr_rbx + lxr_rdx + lxr_rcx + lxr_rax + lxr_r8 + lxr_r9 + lxr_r10 + lxr_r11 + lxr_r12 + lxr_r13 + lxr_r14 + lxr_r15 + lxr_rip + lxr_orig_rax + +lx_sigstack_t SIZEOF_LX_SIGSTACK_T + retaddr + si + uc + fpstate + pad diff --git a/usr/src/lib/brand/lx/lx_brand/common/aio.c b/usr/src/lib/brand/lx/lx_brand/common/aio.c new file mode 100644 index 0000000000..604ba76796 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/aio.c @@ -0,0 +1,519 @@ +/* + * 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. All rights reserved. + */ + +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/mman.h> +#include <thread.h> +#include <synch.h> +#include <port.h> +#include <aio.h> +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <strings.h> +#include <stdlib.h> +#include <sys/lx_types.h> +#include <sys/lx_debug.h> +#include <sys/lx_stat.h> +#include <sys/lx_syscall.h> +#include <sys/lx_misc.h> +#include <sys/lx_aio.h> + +/* + * We implement the Linux asynchronous I/O system calls by using the POSIX + * asynchronous I/O facilities together with event port notification. This + * approach allows us to broadly approximate Linux semantics, but see + * lx_io_cancel() for some limitations. + */ + +struct lx_aiocb { + struct aiocb lxaiocb_cb; /* POSIX AIO control block */ + struct lx_aiocb *lxaiocb_next; /* next outstanding/free I/O */ + struct lx_aiocb *lxaiocb_prev; /* prev outstanding I/O */ + uintptr_t lxaiocb_iocbp; /* pointer to lx_iocb_t */ + uintptr_t lxaiocb_data; /* data payload */ +}; + +struct lx_aio_context { + mutex_t lxaio_lock; /* lock protecting context */ + boolean_t lxaio_destroying; /* boolean: being destroyed */ + cond_t lxaio_destroyer; /* destroyer's condvar */ + int lxaio_waiters; /* number of waiters */ + size_t lxaio_size; /* total size of mapping */ + int lxaio_port; /* port for completion */ + lx_aiocb_t *lxaio_outstanding; /* outstanding I/O */ + lx_aiocb_t *lxaio_free; /* free I/O control blocks */ + int lxaio_nevents; /* max number of events */ +}; + +int lx_aio_max_nr = 65536; + +long +lx_io_setup(unsigned int nr_events, lx_aio_context_t **ctxp) +{ + lx_aio_context_t *ctx; + lx_aiocb_t *lxcbs; + uintptr_t check; + size_t size; + int i; + + if (uucopy(ctxp, &check, sizeof (ctxp)) != 0) + return (-EFAULT); + + if (check != NULL || nr_events == 0 || nr_events > lx_aio_max_nr) + return (-EINVAL); + + /* + * We can't actually malloc from the brand library, which makes this + * a tad rocky -- but we're saved from complexity in no small measure + * by the fact that the cap on the number of concurrent events must + * be specified a priori; we use that to determine the amount of + * memory we need and mmap() it upfront. + */ + size = sizeof (lx_aio_context_t) + nr_events * sizeof (lx_aiocb_t); + + /* LINTED - alignment */ + if ((ctx = (lx_aio_context_t *)mmap(0, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0)) == (lx_aio_context_t *)-1) { + return (-ENOMEM); + } + + ctx->lxaio_size = size; + ctx->lxaio_nevents = nr_events; + + if ((ctx->lxaio_port = port_create()) == -1) { + munmap((caddr_t)ctx, ctx->lxaio_size); + return (-EAGAIN); + } + + (void) mutex_init(&ctx->lxaio_lock, USYNC_THREAD, NULL); + + /* + * Link up the free list. + */ + lxcbs = (lx_aiocb_t *)((uintptr_t)ctx + sizeof (lx_aio_context_t)); + + for (i = 0; i < nr_events - 1; i++) + lxcbs[i].lxaiocb_next = &lxcbs[i + 1]; + + ctx->lxaio_free = &lxcbs[0]; + + if (uucopy(&ctx, ctxp, sizeof (ctxp)) != 0) { + (void) close(ctx->lxaio_port); + munmap((caddr_t)ctx, ctx->lxaio_size); + return (-EFAULT); + } + + return (0); +} + +long +lx_io_submit(lx_aio_context_t *ctx, long nr, uintptr_t **bpp) +{ + int processed = 0, err = 0, i; + port_notify_t notify; + lx_aiocb_t *lxcb; + lx_iocb_t **iocbpp, iocb, *iocbp = &iocb; + struct aiocb *aiocb; + + if (nr <= 0 || ctx == NULL) + return (-EINVAL); + + if ((iocbpp = (lx_iocb_t **)malloc(nr * sizeof (uintptr_t))) == NULL) + return (-EAGAIN); + + if (uucopy(bpp, iocbpp, nr * sizeof (uintptr_t)) != 0) { + free(iocbpp); + return (-EFAULT); + } + + mutex_lock(&ctx->lxaio_lock); + + for (i = 0; i < nr; i++) { + if ((lxcb = ctx->lxaio_free) == NULL) { + err = EAGAIN; + break; + } + + if (uucopy(iocbpp[i], &iocb, sizeof (iocb)) != 0) { + err = EFAULT; + break; + } + + lxcb->lxaiocb_iocbp = (uintptr_t)iocbpp[i]; + lxcb->lxaiocb_data = iocbp->lxiocb_data; + + /* + * We don't currently support eventfd-based notification. + */ + if (iocbp->lxiocb_flags & LX_IOCB_FLAG_RESFD) { + err = ENOSYS; + break; + } + + notify.portnfy_port = ctx->lxaio_port; + notify.portnfy_user = lxcb; + + aiocb = &lxcb->lxaiocb_cb; + aiocb->aio_fildes = iocbp->lxiocb_fd; + aiocb->aio_sigevent.sigev_notify = SIGEV_PORT; + aiocb->aio_sigevent.sigev_value.sival_ptr = ¬ify; + + switch (iocbp->lxiocb_op) { + case LX_IOCB_CMD_FSYNC: + case LX_IOCB_CMD_FDSYNC: + err = aio_fsync(iocbp->lxiocb_op == LX_IOCB_CMD_FSYNC ? + O_SYNC : O_DSYNC, aiocb); + break; + + case LX_IOCB_CMD_PREAD: + case LX_IOCB_CMD_PWRITE: + aiocb->aio_offset = iocbp->lxiocb_offset; + + if (aiocb->aio_nbytes > LONG_MAX) { + err = EINVAL; + break; + } + + aiocb->aio_nbytes = iocbp->lxiocb_nbytes; + + if ((uintptr_t)iocbp->lxiocb_buf > ULONG_MAX) { + err = EINVAL; + break; + } + + aiocb->aio_buf = (void *)(uintptr_t)iocbp->lxiocb_buf; + aiocb->aio_reqprio = 0; + + if (iocbp->lxiocb_op == LX_IOCB_CMD_PREAD) { + err = aio_read(aiocb); + } else { + err = aio_write(aiocb); + } + + break; + + case LX_IOCB_CMD_NOOP: + /* + * Yet another whodunit in Adventure Playground: why + * does Linux define an operation -- IOCB_CMD_NOOP -- + * for which it always returns EINVAL?! And what + * could a "no-op" possibly mean for asynchronous I/O + * anyway?! Do nothing... later?! + */ + err = EINVAL; + break; + + case LX_IOCB_CMD_PREADV: + case LX_IOCB_CMD_PWRITEV: + /* + * We don't support asynchronous preadv and pwritev + * (an asynchronous scatter/gather being a somewhat odd + * notion to begin with); we return EINVAL in this + * case, which the caller should be able to deal with. + */ + err = EINVAL; + break; + + default: + err = EINVAL; + break; + } + + if (err == -1) + err = errno; + + if (err != 0) + break; + + /* + * We successfully enqueued I/O. Take our control block off + * of the free list and transition it to our list of + * outstanding I/O. + */ + ctx->lxaio_free = lxcb->lxaiocb_next; + lxcb->lxaiocb_next = ctx->lxaio_outstanding; + + if (ctx->lxaio_outstanding != NULL) + ctx->lxaio_outstanding->lxaiocb_prev = lxcb; + + ctx->lxaio_outstanding = lxcb; + processed++; + } + + mutex_unlock(&ctx->lxaio_lock); + + free(iocbpp); + if (processed == 0) + return (-err); + + return (processed); +} + +long +lx_io_getevents(lx_aio_context_t *ctx, long min_nr, long nr, + lx_io_event_t *events, struct timespec *timeout) +{ + port_event_t *list; + lx_io_event_t *out; + unsigned int nget = min_nr; + int rval, i, err; + uint32_t max = nr; + + if (nr > ctx->lxaio_nevents) + return (-EINVAL); + + list = SAFE_ALLOCA(nr * sizeof (port_event_t)); + + /* + * Grab the lock associated with the context to bump the number of + * waiters. This is needed in case this context is destroyed while + * we're still waiting on it. + */ + mutex_lock(&ctx->lxaio_lock); + + if (ctx->lxaio_destroying) { + mutex_unlock(&ctx->lxaio_lock); + return (-EINVAL); + } + + ctx->lxaio_waiters++; + mutex_unlock(&ctx->lxaio_lock); + + rval = port_getn(ctx->lxaio_port, list, max, &nget, timeout); + err = errno; + + mutex_lock(&ctx->lxaio_lock); + + assert(ctx->lxaio_waiters > 0); + ctx->lxaio_waiters--; + + if ((rval == -1 && err != ETIME) || nget == 0 || + (nget == 1 && list[0].portev_source == PORT_SOURCE_ALERT)) { + /* + * If we're being destroyed, kick our waiter and clear out with + * EINVAL -- this is effectively an application-level race. + */ + if (ctx->lxaio_destroying) { + cond_signal(&ctx->lxaio_destroyer); + err = EINVAL; + } + + mutex_unlock(&ctx->lxaio_lock); + + return (nget == 0 ? 0 : -err); + } + + out = SAFE_ALLOCA(nget * sizeof (lx_io_event_t)); + + /* + * For each returned event, translate it into the Linux event in our + * stack-based buffer. As we're doing this, we also free the lxcb by + * moving it from the outstanding list to the free list. + */ + for (i = 0; i < nget; i++) { + port_event_t *pe = &list[i]; + lx_io_event_t *lxe = &out[i]; + struct aiocb *aiocb; + lx_aiocb_t *lxcb; + + lxcb = pe->portev_user; + aiocb = (struct aiocb *)pe->portev_object; + + assert(pe->portev_source == PORT_SOURCE_AIO); + assert(aiocb == &lxcb->lxaiocb_cb); + + lxe->lxioe_data = lxcb->lxaiocb_data; + lxe->lxioe_object = lxcb->lxaiocb_iocbp; + lxe->lxioe_res = aio_return(aiocb); + lxe->lxioe_res2 = 0; + + if (lxcb->lxaiocb_next != NULL) + lxcb->lxaiocb_next->lxaiocb_prev = lxcb->lxaiocb_prev; + + if (lxcb->lxaiocb_prev != NULL) { + lxcb->lxaiocb_prev->lxaiocb_next = lxcb->lxaiocb_next; + } else { + assert(ctx->lxaio_outstanding == lxcb); + ctx->lxaio_outstanding = lxcb->lxaiocb_next; + } + + lxcb->lxaiocb_prev = NULL; + lxcb->lxaiocb_next = ctx->lxaio_free; + ctx->lxaio_free = lxcb; + } + + /* + * Perform one final check for a shutdown -- it's possible that we + * raced with the port transitioning into alert mode, in which case we + * have a blocked destroyer that we need to kick. (Note that we do + * this after having properly cleaned up the completed I/O.) + */ + if (ctx->lxaio_destroying) { + cond_signal(&ctx->lxaio_destroyer); + mutex_unlock(&ctx->lxaio_lock); + return (-EINVAL); + } + + mutex_unlock(&ctx->lxaio_lock); + + if (uucopy(out, events, nget * sizeof (lx_io_event_t)) != 0) + return (-EFAULT); + + return (nget); +} + +/* + * Cancellation is unfortunately problematic for us as the POSIX semantics for + * AIO cancellation differ slightly from the Linux semantics: on Linux, + * io_cancel() regrettably does not use the same mechanism for event + * consumption (that is, as an event retrievable via io_getevents()), but + * rather returns the cancellation event directly from io_cancel(). This is + * in contrast to POSIX AIO cancellation, which does not actually alter the + * notification mechanism: the cancellation is still received via its + * specified notification (i.e., an event port or signal). The unfortunate + * Linux semantics leave us with several (suboptimal) choices: + * + * (1) Cancel the I/O via aio_cancel(), and then somehow attempt to block on + * the asynchronous cancellation notification without otherwise disturbing + * other events that may be pending. + * + * (2) Cancel the I/O via aio_cancel() but ignore (and later, discard) the + * asynchronous cancellation notification. + * + * (3) Explicitly fail to cancel any asynchronous I/O by having io_cancel() + * always return EAGAIN. + * + * While the third option is the least satisfying from an engineering + * perspective, it is also entirely within the rights of the interface (which + * may return EAGAIN to merely denote that the specified I/O "was not + * canceled") and has the added advantage of being entirely honest. (This is + * in stark contrast to the first two options, each of which tries to tell + * small lies that seem to sure to end in elaborate webs of deceit.) Honesty + * is the best policy; after checking that the specified I/O is outstanding, + * we fail with EAGAIN. + */ +/*ARGSUSED*/ +long +lx_io_cancel(lx_aio_context_t *ctx, lx_iocb_t *iocbp, lx_io_event_t *result) +{ + lx_iocb_t iocb; + lx_aiocb_t *lxcb; + + if (uucopy(iocbp, &iocb, sizeof (lx_iocb_t)) != 0) + return (-EFAULT); + + mutex_lock(&ctx->lxaio_lock); + + if (ctx->lxaio_destroying) { + mutex_unlock(&ctx->lxaio_lock); + return (-EINVAL); + } + + for (lxcb = ctx->lxaio_outstanding; lxcb != NULL && + lxcb->lxaiocb_iocbp != (uintptr_t)iocbp; lxcb = lxcb->lxaiocb_next) + continue; + + mutex_unlock(&ctx->lxaio_lock); + + if (lxcb == NULL) + return (-EINVAL); + + /* + * Congratulations on your hard-won EAGAIN! + */ + return (-EAGAIN); +} + +/* + * As is often the case, the destruction case makes everything a lot more + * complicated. In this case, io_destroy() is defined to block on the + * completion of all outstanding operations. To effect this, we throw the + * event port into the rarely-used alert mode -- invented long ago for just + * this purpose -- thereby kicking any waiters out of their port_get(). + */ +long +lx_io_destroy(lx_aio_context_t *ctx) +{ + lx_aiocb_t *lxcb; + unsigned int nget = 0, nr; + int port = ctx->lxaio_port; + + mutex_lock(&ctx->lxaio_lock); + + if (ctx->lxaio_destroying) { + mutex_unlock(&ctx->lxaio_lock); + return (-EINVAL); + } + + ctx->lxaio_destroying = B_TRUE; + + if (ctx->lxaio_waiters) { + /* + * If we have waiters, put the port into alert mode. + */ + (void) port_alert(port, PORT_ALERT_SET, B_TRUE, NULL); + + while (ctx->lxaio_waiters) + cond_wait(&ctx->lxaio_destroyer, &ctx->lxaio_lock); + + /* + * Transition the port out of alert mode: we will need to + * block on the port ourselves for any outstanding I/O. + */ + (void) port_alert(port, PORT_ALERT_SET, B_FALSE, NULL); + } + + /* + * We have no waiters and we never will again -- we can be assured + * that our list of outstanding I/Os is now completely static and it's + * now safe to iterate over our outstanding I/Os and aio_cancel() them. + */ + for (lxcb = ctx->lxaio_outstanding; lxcb != NULL; + lxcb = lxcb->lxaiocb_next) { + struct aiocb *aiocb = &lxcb->lxaiocb_cb; + + /* + * Surely a new bureaucratic low even for POSIX that we must + * specify both the file descriptor and the structure that + * must contain the file desctiptor... + */ + (void) aio_cancel(aiocb->aio_fildes, aiocb); + nget++; + } + + if (nget != 0) { + port_event_t *list = SAFE_ALLOCA(nget * sizeof (port_event_t)); + int rval; + + do { + rval = port_getn(port, list, nr = nget, &nget, NULL); + } while (rval == -1 && errno == EINTR); + + assert(rval == 0); + assert(nget == nr); + } + + /* + * I/Os are either cancelled or completed. We can safely close our + * port and nuke the mapping that contains our context. + */ + (void) close(ctx->lxaio_port); + munmap((caddr_t)ctx, ctx->lxaio_size); + + return (0); +} 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..81f3bbddb4 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/capabilities.c @@ -0,0 +1,484 @@ +/* + * 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. + */ + +/* + * LX Brand emulation of capget/capset syscalls + */ + +#include <sys/types.h> +#include <sys/errno.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 + +/* + * Even though we lack mappings for capabilities higher than 36, 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_MAX_VALID 36 + +#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_IPC_CONFIG, + PRIV_SYS_DL_CONFIG, + NULL +}; +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) \ + do { \ + ptr = SAFE_ALLOCA(sizeof (lx_cap_privs_t) + \ + (3 * lx_cap_priv_size)); \ + if (ptr != NULL) { \ + ptr->p_effective = (void *) ptr + \ + sizeof (lx_cap_privs_t); \ + ptr->p_permitted = (void *) ptr + \ + sizeof (lx_cap_privs_t) + \ + lx_cap_priv_size;\ + ptr->p_inheritable = (void *) ptr + \ + sizeof (lx_cap_privs_t) + \ + 2 * lx_cap_priv_size; \ + } \ + } while (0) + +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 (lx_cap_mapping[i] == NULL || i > LX_CAP_MAX_VALID) { + /* don't allow setting unsupported caps */ + if (cap_set) + 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) { + priv_delset(priv, lx_cap_mapping[i][j]); + updated = B_TRUE; + } else if (!priv_set && cap_set) { + 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; + 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 */ + 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: + 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..793e2cb181 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/clock.c @@ -0,0 +1,229 @@ +/* + * 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 <errno.h> +#include <string.h> +#include <time.h> +#include <sys/resource.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 + * + * See the comment on clock_highres_timer_create for full details but a zone + * needs the proc_clock_highres privilege to use the CLOCK_HIGHRES clock so it + * will generally be unusable by lx for timer_create. + */ + +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 */ +}; + +/* + * Since the Illumos CLOCK_HIGHRES clock requires elevated privs, which can + * lead to a DOS, we use the only other option (CLOCK_REALTIME) when given + * LX_CLOCK_MONOTONIC. + */ +static int ltos_timer[] = { + CLOCK_REALTIME, + CLOCK_REALTIME, + CLOCK_THREAD_CPUTIME_ID, /* XXX thread, not process but fails */ + CLOCK_THREAD_CPUTIME_ID, + CLOCK_REALTIME, + CLOCK_REALTIME, + CLOCK_REALTIME +}; + +#define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0])) +#define LX_TIMER_MAX (sizeof (ltos_timer) / sizeof (ltos_timer[0])) + +#define LX_SIGEV_PAD_SIZE ((64 - \ + (sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int)) + +typedef struct { + union sigval lx_sigev_value; /* same layout for both */ + int lx_sigev_signo; + int lx_sigev_notify; + union { + int lx_pad[LX_SIGEV_PAD_SIZE]; + int lx_tid; + struct { + void (*lx_notify_function)(union sigval); + void *lx_notify_attribute; + } lx_sigev_thread; + } lx_sigev_un; +} lx_sigevent_t; + +/* sigevent sigev_notify conversion table */ +static int ltos_sigev[] = { + SIGEV_SIGNAL, + SIGEV_NONE, + SIGEV_THREAD, + 0, /* Linux skips event 3 */ + SIGEV_THREAD /* the Linux SIGEV_THREAD_ID */ +}; + +#define LX_SIGEV_MAX (sizeof (ltos_sigev) / sizeof (ltos_sigev[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); +} + +/* + * The Illumos timer_create man page says it accepts the following clocks: + * CLOCK_REALTIME (3) wall clock + * CLOCK_VIRTUAL (1) user CPU usage clock - No Backend + * CLOCK_PROF (2) user and system CPU usage clock - No Backend + * CLOCK_HIGHRES (4) non-adjustable, high-resolution clock + * However, in reality the Illumos timer_create only accepts CLOCK_REALTIME + * and CLOCK_HIGHRES, and since we can't use CLOCK_HIGHRES in a zone, we're + * down to one clock. + */ +long +lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid) +{ + lx_sigevent_t lev; + struct sigevent sev; + + if (clock < 0 || clock >= LX_TIMER_MAX) + return (-EINVAL); + + /* We have to convert the Linux sigevent layout to the Illumos layout */ + if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0) + return (-EFAULT); + + if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX) + return (-EINVAL); + + sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify]; + sev.sigev_signo = ltos_signo[lev.lx_sigev_signo]; + sev.sigev_value = lev.lx_sigev_value; + + /* + * The sigevent sigev_notify_function and sigev_notify_attributes + * members are not used by timer_create, so no conversion is needed. + */ + + return ((timer_create(ltos_timer[clock], &sev, tid) < 0) ? -errno : 0); +} + +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); +} 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..69a5e55a55 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/clone.c @@ -0,0 +1,728 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#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 <lx_syscall.h> + + +#define SHARED_AS \ + (LX_CLONE_VM | LX_CLONE_FS | LX_CLONE_FILES | LX_CLONE_SIGHAND \ + | LX_CLONE_THREAD) +#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 */ + sigset_t c_sigmask; /* signal mask */ + lx_affmask_t c_affmask; /* CPU affinity mask */ + 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 */ +}; + +/* + * Counter incremented when we vfork(2) ourselves, and decremented when the + * vfork(2)ed child exit(2)s or exec(2)s. + */ +static int is_vforked = 0; + +long +lx_exit(uintptr_t p1) +{ + int status = (int)p1; + lx_tsd_t *lx_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 (is_vforked != 0) { + is_vforked--; + _exit(status); + } + + lx_tsd = lx_get_tsd(); + + 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; + + /* + * If we are a vfork(2)ed child, we need to exit as quickly and + * cleanly as possible to avoid corrupting our parent. + */ + if (is_vforked != 0) { + is_vforked--; + _exit(status); + } + + lx_tsd = lx_get_tsd(); + + 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); + } + + if (lx_sched_setaffinity(0, sizeof (cs->c_affmask), + (uintptr_t)&cs->c_affmask) != 0) { + *(cs->c_clone_res) = -errno; + + lx_err_fatal("Unable to set affinity mask in child thread: %s", + strerror(errno)); + } + + /* + * 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); + + if (sigprocmask(SIG_SETMASK, &cs->c_sigmask, NULL) < 0) { + *(cs->c_clone_res) = -errno; + + lx_err_fatal("Unable to release held signals for child " + "thread: %s", strerror(errno)); + } + + /* + * 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, and + * switch to the BRAND stack. + */ + cs->c_uc.uc_flags = UC_CPU; + 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. The system call must not return. + */ + if (syscall(SYS_brand, B_JUMP_TO_LINUX, &cs->c_uc) == -1) { + lx_err_fatal("B_JUMP_TO_LINUX failed: %s", + strerror(errno)); + } + abort(); + + /*NOTREACHED*/ + return (NULL); +} + +/* + * 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; + int error = 0; + + 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); + } + + 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)); + + /* + * Handle a fork(2) operation here. If this is not a fork, a new + * thread will be created after this block. + */ + if (IS_FORK(flags) || IS_VFORK(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. + */ + _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); + is_vforked++; + rval = vforkx(fork_flags); + if (rval != 0) { + 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) { + if (!IS_VFORK(flags) || rval < 0) { + /* + * Run the stack management postfork handler in + * the parent. If this was a vfork(2), we only + * run it in the parent if the fork operation + * failed; the vfork(2) child has already run + * it for our address space. + */ + 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)); + } + + if (rval > 0) { + lx_ptrace_stop_if_option(ptrace_event, B_FALSE, + (ulong_t)rval, NULL); + } + + /* + * Re-enable signal delivery in the parent process. + */ + _sigon(); + + return ((rval < 0) ? -errno : rval); + } + + /* + * The rest of this block runs only within the new child + * process. + */ + + /* + * Run the stack management postfork handler in the child. + */ + lx_stack_postfork(); + + if (!IS_VFORK(flags)) { + /* + * 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] -= 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; + } + + /* + * 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; + } + + /* + * Stop for ptrace if required. + */ + lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL); + + /* + * Re-enable signal delivery in the child process. + */ + _sigon(); + + /* + * The child process returns via the regular emulated system + * call path: + */ + return (0); + } + + /* + * We have very restricted support.... only exactly these flags are + * supported + */ + if (((flags & SHARED_AS) != SHARED_AS)) { + lx_unsupported("clone(2) 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: %s", strerror(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); + } + + if (lx_sched_getaffinity(0, sizeof (cs->c_affmask), + (uintptr_t)&cs->c_affmask) == -1) { + lx_err_fatal("Unable to get affinity mask for parent " + "thread: %s", strerror(errno)); + } + + clone_res = 0; + + (void) sigfillset(&sigmask); + + /* + * Block all signals because the thread we create won't be able to + * properly handle them until it's fully set up. + */ + if (sigprocmask(SIG_BLOCK, &sigmask, &osigmask) < 0) { + lx_debug("lx_clone sigprocmask() failed: %s", strerror(errno)); + free(cs->c_lx_tsd); + free(cs); + return (-errno); + } + cs->c_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, NULL, clone_start, cs, THR_DETACHED, &tid); + + /* + * If the thread did not start, free the resources we allocated: + */ + if (rval == -1) { + error = errno; + (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 (-error); + } +} 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..7e58a0f941 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/debug.c @@ -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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2014 Joyent, Inc. All rights reserved. + */ + +#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 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_enable(void) +{ + /* send all debugging output to /dev/tty */ + lx_debug_path = "/dev/tty"; + lx_debug("lx_debug: debugging output enabled: %s", lx_debug_path); +} + +void +lx_debug_init(void) +{ + /* + * 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 (getenv("LX_DTRACE") != NULL || !lx_dtrace_lazyload) { + extern void _init(void); + _init(); + } + + if (getenv("LX_DEBUG") == NULL) + 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 = getenv("LX_DEBUG_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/fcntl.c b/usr/src/lib/brand/lx/lx_brand/common/fcntl.c new file mode 100644 index 0000000000..7b6cd4d5f2 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/fcntl.c @@ -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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +#include <sys/types.h> +#include <sys/filio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stropts.h> +#include <libintl.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> + +#include <sys/lx_fcntl.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +long +lx_dup2(uintptr_t p1, uintptr_t p2) +{ + int oldfd = (int)p1; + int newfd = (int)p2; + int rc; + + rc = fcntl(oldfd, F_DUP2FD, newfd); + return ((rc == -1) ? -errno : rc); +} + +long +lx_dup3(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int oldfd = (int)p1; + int newfd = (int)p2; + int flags = (int)p3; + int rc; + + /* The only valid flag is O_CLOEXEC. */ + if (flags & ~LX_O_CLOEXEC) + return (-EINVAL); + + if (oldfd == newfd) + return (-EINVAL); + + rc = fcntl(oldfd, (flags == 0) ? F_DUP2FD : F_DUP2FD_CLOEXEC, newfd); + return ((rc == -1) ? -errno : rc); +} + +/* + * flock() applies or removes an advisory lock on the file + * associated with the file descriptor fd. + * + * operation is: LX_LOCK_SH, LX_LOCK_EX, LX_LOCK_UN, LX_LOCK_NB + */ +long +lx_flock(uintptr_t p1, uintptr_t p2) +{ + int fd = (int)p1; + int operation = (int)p2; + struct flock fl; + int cmd; + int ret; + + if (operation & LX_LOCK_NB) { + cmd = F_FLOCK; + operation &= ~LX_LOCK_NB; /* turn off this bit */ + } else { + cmd = F_FLOCKW; + } + + switch (operation) { + case LX_LOCK_UN: + fl.l_type = F_UNLCK; + break; + case LX_LOCK_SH: + fl.l_type = F_RDLCK; + break; + case LX_LOCK_EX: + fl.l_type = F_WRLCK; + break; + default: + return (-EINVAL); + } + + fl.l_whence = 0; + fl.l_start = 0; + fl.l_len = 0; + fl.l_sysid = 0; + fl.l_pid = 0; + + ret = fcntl(fd, cmd, &fl); + + return ((ret == -1) ? -errno : ret); +} + +/* + * Based on Illumos posix_fadvise which does nothing. The only difference is + * that on Linux an fd refering to a pipe or FIFO returns EINVAL. + * The Linux POSIX_FADV_* values are the same as the Illumos values. + * See how glibc calls fadvise64; the offeset is a 64bit value, but the length + * is not, whereas fadvise64_64 passes both the offset and length as 64bit + * values. + */ +/* ARGSUSED */ +long +lx_fadvise64(uintptr_t p1, off64_t p2, uintptr_t p3, uintptr_t p4) +{ + int fd = (int)p1; + int advice = (int)p4; + int32_t len = (int32_t)p3; + struct stat64 statb; + + switch (advice) { + case POSIX_FADV_NORMAL: + case POSIX_FADV_RANDOM: + case POSIX_FADV_SEQUENTIAL: + case POSIX_FADV_WILLNEED: + case POSIX_FADV_DONTNEED: + case POSIX_FADV_NOREUSE: + break; + default: + return (-EINVAL); + } + if (len < 0) + return (-EINVAL); + if (fstat64(fd, &statb) != 0) + return (-EBADF); + if (S_ISFIFO(statb.st_mode)) + return (-ESPIPE); + return (0); +} + +long +lx_fadvise64_64(uintptr_t p1, off64_t p2, off64_t p3, uintptr_t p4) +{ + + if (p3 < 0) + return (-EINVAL); + + return (lx_fadvise64(p1, p2, 0, p4)); +} 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..0654ac5c3e --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/file.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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +#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); +} + +/* + * Convert linux LX_AT_* flags to solaris AT_* flags but skip verifying allowed + * flags have been passed. This also allows EACCESS/REMOVEDIR to be translated + * correctly since on linux they have the same value. + * + * Some code can actually pass in other bits in the flag. We may have to simply + * ignore these, as indicated by the enforce parameter. See lx_fchmodat for + * another example of this type of behavior. + */ +int +ltos_at_flag(int lflag, int allow, boolean_t enforce) +{ + int sflag = 0; + + if ((lflag & LX_AT_EACCESS) && (allow & AT_EACCESS)) { + lflag &= ~LX_AT_EACCESS; + sflag |= AT_EACCESS; + } + + if ((lflag & LX_AT_REMOVEDIR) && (allow & AT_REMOVEDIR)) { + lflag &= ~LX_AT_REMOVEDIR; + sflag |= AT_REMOVEDIR; + } + + if ((lflag & LX_AT_SYMLINK_NOFOLLOW) && (allow & AT_SYMLINK_NOFOLLOW)) { + lflag &= ~LX_AT_SYMLINK_NOFOLLOW; + sflag |= AT_SYMLINK_NOFOLLOW; + } + + /* right now solaris doesn't have a _FOLLOW flag, so use a fake one */ + if ((lflag & LX_AT_SYMLINK_FOLLOW) && (allow & LX_AT_SYMLINK_FOLLOW)) { + lflag &= ~LX_AT_SYMLINK_FOLLOW; + sflag |= LX_AT_SYMLINK_FOLLOW; + } + + /* If lflag is not zero than some flags did not hit the above code. */ + if (enforce && lflag) + return (-EINVAL); + + return (sflag); +} + + +/* + * Miscellaneous file-related system calls. + */ + +/* + * On Linux, even root cannot create a link to a directory, so we have to + * add an explicit check. + */ +long +lx_link(uintptr_t p1, uintptr_t p2) +{ + char *from = (char *)p1; + char *to = (char *)p2; + struct stat64 statbuf; + + if ((stat64(from, &statbuf) == 0) && S_ISDIR(statbuf.st_mode)) + return (-EPERM); + + return (link(from, to) ? -errno : 0); +} + +/* + * On Linux, an unlink of a directory returns EISDIR, not EPERM. + */ +long +lx_unlink(uintptr_t p) +{ + char *pathname = (char *)p; + struct stat64 statbuf; + + if ((lstat64(pathname, &statbuf) == 0) && S_ISDIR(statbuf.st_mode)) + return (-EISDIR); + + /* + * Some versions of the zone's /dev/log setup code (e.g. syslog-ng) get + * cranky if they can't cleanup /dev/log so we lie and tell them they + * succeeded. + */ + if (pathname != NULL) { + char p[MAXPATHLEN]; + + if (uucopystr((void *)pathname, p, sizeof (p)) < 0) + return (-errno); + if (strcmp(p, "/dev/log") == 0) + return (0); + } + + return (unlink(pathname) ? -errno : 0); +} + +long +lx_unlinkat(uintptr_t ext1, uintptr_t p1, uintptr_t p2) +{ + int atfd = (int)ext1; + char *pathname = (char *)p1; + int flag = (int)p2; + struct stat64 statbuf; + + if (atfd == LX_AT_FDCWD) + atfd = AT_FDCWD; + + flag = ltos_at_flag(flag, AT_REMOVEDIR, B_TRUE); + if (flag < 0) + return (-EINVAL); + + if (!(flag & AT_REMOVEDIR)) { + /* Behave like unlink() */ + if ((fstatat64(atfd, pathname, &statbuf, AT_SYMLINK_NOFOLLOW) == + 0) && S_ISDIR(statbuf.st_mode)) + return (-EISDIR); + + if (pathname != NULL) { + char p[MAXPATHLEN]; + + if (uucopystr((void *)pathname, p, sizeof (p)) < 0) + return (-errno); + if (strcmp(p, "/dev/log") == 0) + return (0); + } + } + + return (unlinkat(atfd, pathname, flag) ? -errno : 0); +} + +/* + * 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); +} + +#if defined(_ILP32) +/* + * llseek() - The Linux implementation takes an additional parameter, which is + * the resulting position in the file. + */ +long +lx_llseek(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5) +{ + offset_t ret; + offset_t *res = (offset_t *)p4; + + /* SEEK_DATA and SEEK_HOLE are only valid in Solaris */ + if ((int)p5 > SEEK_END) + return (-EINVAL); + + if ((ret = llseek((int)p1, LX_32TO64(p3, p2), p5)) < 0) + return (-errno); + + *res = ret; + return (0); +} +#endif + +/* + * seek() - For 32-bit lx, when the resultant file offset cannot be represented + * in 32 bits, Linux performs the seek but Illumos doesn't, though both set + * EOVERFLOW. We call llseek() and then check to see if we need to return + * EOVERFLOW. + */ +long +lx_lseek(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + offset_t offset = (offset_t)(off_t)(p2); /* sign extend */ + offset_t ret; +#if defined(_ILP32) + off_t ret32; +#endif + + /* SEEK_DATA and SEEK_HOLE are only valid in Illumos */ + if ((int)p3 > SEEK_END) + return (-EINVAL); + + if ((ret = llseek((int)p1, offset, p3)) < 0) + return (-errno); + +#if defined(_LP64) + return (ret); +#else + ret32 = (off_t)ret; + if ((offset_t)ret32 == ret) + return (ret32); + else + return (-EOVERFLOW); +#endif +} + +/* + * Neither Illumos nor Linux actually returns anything to the caller, but glibc + * expects to see SOME value returned, so placate it and return 0. + */ +long +lx_sync(void) +{ + sync(); + 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); +} + +/* + * For Illumos, access() does this: + * If the process has appropriate privileges, an implementation may indicate + * success for X_OK even if none of the execute file permission bits are set. + * + * But for Linux, access() does this: + * If the calling process is privileged (i.e., its real UID is zero), then + * an X_OK check is successful for a regular file if execute permission is + * enabled for any of the file owner, group, or other. + * + * Linux used to behave more like Illumos on older kernels: + * In kernel 2.4 (and earlier) there is some strangeness in the handling + * of X_OK tests for superuser. If all categories of execute permission + * are disabled for a nondirectory file, then the only access() test that + * returns -1 is when mode is specified as just X_OK; if R_OK or W_OK is + * also specified in mode, then access() returns 0 for such files. Early + * 2.6 kernels (up to and including 2.6.3) also behaved in the same way as + * kernel 2.4. + * + * So we need to handle the case where a privileged process is checking for + * X_OK but none of the execute bits are set on the file. We'll keep the old + * 2.4 behavior for 2.4 emulation but use the new behavior for any other + * kernel rev. (since 2.6.3 is uninteresting, no relevant distros use that). + */ +long +lx_access(uintptr_t p1, uintptr_t p2) +{ + char *path = (char *)p1; + int mode = (mode_t)p2; + int ret; + + ret = access(path, mode); + + if (ret == 0 && (mode & X_OK) && strncmp(lx_release, "2.4", 3) != 0 && + getuid() == 0) { + /* check for incorrect execute success */ + struct stat64 sb; + + if (stat64(path, &sb) < 0) + return (-errno); + + if ((sb.st_mode & S_IFMT) == S_IFREG && + !(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + /* no execute bits are set in the mode */ + return (-EACCES); + } + } + + return (ret ? -errno : 0); + +} + +long +lx_faccessat(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int atfd = (int)p1; + char *path = (char *)p2; + int mode = (mode_t)p3; + int flag = (int)p4; + + if (atfd == LX_AT_FDCWD) + atfd = AT_FDCWD; + + flag = ltos_at_flag(flag, AT_EACCESS, B_FALSE); + if (flag < 0) + return (-EINVAL); + + return (faccessat(atfd, path, mode, flag) ? -errno : 0); +} + +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)); +} + +long +lx_symlinkat(uintptr_t p1, uintptr_t ext1, uintptr_t p2) +{ + int atfd = (int)ext1; + char pathbuf[MAXPATHLEN]; + int ret; + + ret = getpathat(atfd, p2, pathbuf, sizeof (pathbuf)); + if (ret < 0) { + if (ret == -EBADF) { + /* + * Try to figure out correct Linux errno. We know path + * is relative. Check if we have a fd for a dir which + * has been removed. + */ + if (atfd != -1 && lx_fd_to_path(atfd, pathbuf, + sizeof (pathbuf)) == NULL) + ret = -ENOENT; + } + return (ret); + } + + return (symlink((char *)p1, pathbuf) ? -errno : 0); +} + +long +lx_linkat(uintptr_t ext1, uintptr_t p1, uintptr_t ext2, uintptr_t p2, + uintptr_t p3) +{ + int atfd1 = (int)ext1; + int atfd2 = (int)ext2; + char pathbuf1[MAXPATHLEN]; + char pathbuf2[MAXPATHLEN]; + int ret; + + /* + * The flag specifies whether the hardlink will point to a symlink or + * not, on solaris the default behaviour of link() is to dereference a + * symlink and there is no obvious way to trigger the other behaviour. + * So for now we just ignore this flag and act like link(). + */ + int flag = p3; + + /* return proper error for invalid flags */ + if ((flag & ~(LX_AT_SYMLINK_FOLLOW | LX_AT_EMPTY_PATH)) != 0) + return (-EINVAL); + + ret = getpathat(atfd1, p1, pathbuf1, sizeof (pathbuf1)); + if (ret < 0) { + if (ret == -EBADF) { + /* + * Try to figure out correct Linux errno. We know path + * is relative. Check if we have a fd for a dir which + * has been removed. + */ + if (atfd1 != -1 && lx_fd_to_path(atfd1, pathbuf1, + sizeof (pathbuf1)) == NULL) + ret = -ENOENT; + } + return (ret); + } + + ret = getpathat(atfd2, p2, pathbuf2, sizeof (pathbuf2)); + if (ret < 0) { + if (ret == -EBADF) { + /* + * Try to figure out correct Linux errno. We know path + * is relative. Check if we have a fd for a dir which + * has been removed. + */ + if (atfd2 != -1 && lx_fd_to_path(atfd2, pathbuf2, + sizeof (pathbuf2)) == NULL) + ret = -ENOENT; + } + return (ret); + } + + return (lx_link((uintptr_t)pathbuf1, (uintptr_t)pathbuf2)); +} + +long +lx_readlink(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int ret; + + if ((size_t)p3 <= 0) + return (-EINVAL); + + ret = readlink((char *)p1, (char *)p2, (size_t)p3); + if (ret < 0) + return (-errno); + + return (ret); +} + +long +lx_readlinkat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int atfd = (int)ext1; + char pathbuf[MAXPATHLEN]; + int ret; + + if ((size_t)p3 <= 0) + return (-EINVAL); + + ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf)); + if (ret < 0) + return (ret); + + ret = readlink(pathbuf, (char *)p2, (size_t)p3); + if (ret < 0) + return (-errno); + + return (ret); +} 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..7ade1a1d5b --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/fork.c @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#include <errno.h> +#include <unistd.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. + * + * For vfork(), we have a serious problem because the child is not allowed to + * return from the current frame because it will corrupt the parent's stack. + * Since the semantics of vfork() are rather ill-defined (other than "it's + * faster than fork"), we should theoretically be safe by falling back to + * fork1(). + */ +static long +lx_fork_common(boolean_t is_vfork) +{ + int ret; + int ptopt = is_vfork ? LX_PTRACE_O_TRACEVFORK : LX_PTRACE_O_TRACEFORK; + + /* + * Inform the in-kernel ptrace(2) subsystem that we are about to + * emulate fork(2). + */ + lx_ptrace_clone_begin(ptopt, B_FALSE); + + /* + * 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(ptopt, 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(ptopt, 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_fork(void) +{ + return (lx_fork_common(B_FALSE)); +} + +long +lx_vfork(void) +{ + return (lx_fork_common(B_TRUE)); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/id.c b/usr/src/lib/brand/lx/lx_brand/common/id.c new file mode 100644 index 0000000000..cd5baefa7d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/id.c @@ -0,0 +1,271 @@ +/* + * 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 <sys/types.h> +#include <fcntl.h> +#include <procfs.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/zone.h> +#include <sys/lx_types.h> +#include <sys/lx_syscall.h> +#include <sys/cred_impl.h> +#include <sys/policy.h> +#include <sys/ucred.h> +#include <sys/syscall.h> +#include <alloca.h> +#include <errno.h> +#include <ucred.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/lx_misc.h> + + +long +lx_setuid16(uintptr_t uid) +{ + return ((setuid(LX_UID16_TO_UID32((lx_uid16_t)uid))) ? -errno : 0); +} + +long +lx_getuid16(void) +{ + return ((int)LX_UID32_TO_UID16(getuid())); +} + +long +lx_setgid16(uintptr_t gid) +{ + return ((setgid(LX_GID16_TO_GID32((lx_gid16_t)gid))) ? -errno : 0); +} + +long +lx_getgid16(void) +{ + return ((int)LX_GID32_TO_GID16(getgid())); +} + +long +lx_geteuid16(void) +{ + return ((int)LX_UID32_TO_UID16(geteuid())); +} + +long +lx_getegid16(void) +{ + return ((int)LX_GID32_TO_GID16(getegid())); +} + +long +lx_geteuid(void) +{ + return ((int)geteuid()); +} + +long +lx_getegid(void) +{ + return ((int)getegid()); +} + +long +lx_getresuid(uintptr_t ruid, uintptr_t euid, uintptr_t suid) +{ + lx_uid_t lx_ruid, lx_euid, lx_suid; + ucred_t *cr; + size_t sz; + + /* + * We allocate a ucred_t ourselves rather than call ucred_get(3C) + * because ucred_get() calls malloc(3C), which the brand library cannot + * use. Because we allocate the space with SAFE_ALLOCA(), there's + * no need to free it when we're done. + */ + sz = ucred_size(); + cr = (ucred_t *)SAFE_ALLOCA(sz); + if (cr == NULL) + return (-ENOMEM); + + if (syscall(SYS_ucredsys, UCREDSYS_UCREDGET, P_MYID, cr) != 0) + return (-errno); + + if (((lx_ruid = (lx_uid_t)ucred_getruid(cr)) == (lx_uid_t)-1) || + ((lx_euid = (lx_uid_t)ucred_geteuid(cr)) == (lx_uid_t)-1) || + ((lx_suid = (lx_uid_t)ucred_getsuid(cr)) == (lx_uid_t)-1)) { + return (-errno); + } + + if (uucopy(&lx_ruid, (void *)ruid, sizeof (lx_uid_t)) != 0) + return (-errno); + + if (uucopy(&lx_euid, (void *)euid, sizeof (lx_uid_t)) != 0) + return (-errno); + + return ((uucopy(&lx_suid, (void *)suid, sizeof (lx_uid_t)) != 0) + ? -errno : 0); +} + +long +lx_getresuid16(uintptr_t ruid16, uintptr_t euid16, uintptr_t suid16) +{ + lx_uid_t lx_ruid, lx_euid, lx_suid; + lx_uid16_t lx_ruid16, lx_euid16, lx_suid16; + int rv; + + if ((rv = lx_getresuid((uintptr_t)&lx_ruid, (uintptr_t)&lx_euid, + (uintptr_t)&lx_suid)) != 0) + return (rv); + + lx_ruid16 = LX_UID32_TO_UID16(lx_ruid); + lx_euid16 = LX_UID32_TO_UID16(lx_euid); + lx_suid16 = LX_UID32_TO_UID16(lx_suid); + + if (uucopy(&lx_ruid16, (void *)ruid16, sizeof (lx_uid16_t)) != 0) + return (-errno); + + if (uucopy(&lx_euid16, (void *)euid16, sizeof (lx_uid16_t)) != 0) + return (-errno); + + return ((uucopy(&lx_suid16, (void *)suid16, sizeof (lx_uid16_t)) != 0) + ? -errno : 0); +} + +long +lx_getresgid(uintptr_t rgid, uintptr_t egid, uintptr_t sgid) +{ + ucred_t *cr; + lx_gid_t lx_rgid, lx_egid, lx_sgid; + size_t sz; + + /* + * We allocate a ucred_t ourselves rather than call ucred_get(3C) + * because ucred_get() calls malloc(3C), which the brand library cannot + * use. Because we allocate the space with SAFE_ALLOCA(), there's + * no need to free it when we're done. + */ + sz = ucred_size(); + cr = (ucred_t *)SAFE_ALLOCA(sz); + if (cr == NULL) + return (-ENOMEM); + + if (syscall(SYS_ucredsys, UCREDSYS_UCREDGET, P_MYID, cr) != 0) + return (-errno); + + if (((lx_rgid = (lx_gid_t)ucred_getrgid(cr)) == (lx_gid_t)-1) || + ((lx_egid = (lx_gid_t)ucred_getegid(cr)) == (lx_gid_t)-1) || + ((lx_sgid = (lx_gid_t)ucred_getsgid(cr)) == (lx_gid_t)-1)) { + return (-errno); + } + + if (uucopy(&lx_rgid, (void *)rgid, sizeof (lx_gid_t)) != 0) + return (-errno); + + if (uucopy(&lx_egid, (void *)egid, sizeof (lx_gid_t)) != 0) + return (-errno); + + return ((uucopy(&lx_sgid, (void *)sgid, sizeof (lx_gid_t)) != 0) + ? -errno : 0); +} + +long +lx_getresgid16(uintptr_t rgid16, uintptr_t egid16, uintptr_t sgid16) +{ + lx_gid_t lx_rgid, lx_egid, lx_sgid; + lx_gid16_t lx_rgid16, lx_egid16, lx_sgid16; + int rv; + + if ((rv = lx_getresgid((uintptr_t)&lx_rgid, (uintptr_t)&lx_egid, + (uintptr_t)&lx_sgid)) != 0) + return (rv); + + lx_rgid16 = LX_UID32_TO_UID16(lx_rgid); + lx_egid16 = LX_UID32_TO_UID16(lx_egid); + lx_sgid16 = LX_UID32_TO_UID16(lx_sgid); + + if (uucopy(&lx_rgid16, (void *)rgid16, sizeof (lx_gid16_t)) != 0) + return (-errno); + + if (uucopy(&lx_egid16, (void *)egid16, sizeof (lx_gid16_t)) != 0) + return (-errno); + + return ((uucopy(&lx_sgid16, (void *)sgid16, sizeof (lx_gid16_t)) != 0) + ? -errno : 0); +} + +long +lx_setreuid16(uintptr_t ruid, uintptr_t euid) +{ + return ((setreuid(LX_UID16_TO_UID32((lx_uid16_t)ruid), + LX_UID16_TO_UID32((lx_uid16_t)euid))) ? -errno : 0); +} + +long +lx_setregid16(uintptr_t rgid, uintptr_t egid) +{ + return ((setregid(LX_UID16_TO_UID32((lx_gid16_t)rgid), + LX_UID16_TO_UID32((lx_gid16_t)egid))) ? -errno : 0); +} + +/* + * The lx brand cannot support the setfs[ug]id16/setfs[ug]id calls as that + * would require significant rework of Solaris' privilege mechanisms, so + * instead return the current effective [ug]id. + * + * In Linux, fsids track effective IDs, so returning the effective IDs works + * as a substitute; returning the current value also denotes failure of the + * call if the caller had specified something different. We don't need to + * worry about setting error codes because the Linux calls don't set any. + */ +/*ARGSUSED*/ +long +lx_setfsuid16(uintptr_t fsuid16) +{ + return (lx_geteuid16()); +} + +/*ARGSUSED*/ +long +lx_setfsgid16(uintptr_t fsgid16) +{ + return (lx_getegid16()); +} + +/*ARGSUSED*/ +long +lx_setfsuid(uintptr_t fsuid) +{ + return (geteuid()); +} + +/*ARGSUSED*/ +long +lx_setfsgid(uintptr_t fsgid) +{ + return (getegid()); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/iovec.c b/usr/src/lib/brand/lx/lx_brand/common/iovec.c new file mode 100644 index 0000000000..b6d0f79c75 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/iovec.c @@ -0,0 +1,309 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#include <errno.h> +#include <unistd.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <alloca.h> +#include <string.h> +#include <sys/lx_syscall.h> +#include <sys/lx_misc.h> +#include <sys/lx_types.h> + +static int +lx_is_directory(int fd) +{ + struct stat64 sbuf; + + if (fstat64(fd, &sbuf) < 0) + sbuf.st_mode = 0; + + return ((sbuf.st_mode & S_IFMT) == S_IFDIR); +} + +#if defined(_LP64) +long +lx_pread(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int fd = (int)p1; + void *buf = (void *)p2; + size_t nbyte = (size_t)p3; + off64_t off = (off64_t)p4; + ssize_t ret; + + if (lx_is_directory(fd)) + return (-EISDIR); + + ret = pread64(fd, buf, nbyte, off); + + if (ret < 0) + return (-errno); + + return (ret); +} + +/* + * On Linux, the pwrite(2) system call behaves identically to Solaris except + * in the case of the file being opened with O_APPEND. In that case Linux's + * pwrite(2) ignores the offset parameter and instead appends the data to the + * file without modifying the current seek pointer. + */ +long +lx_pwrite(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int fd = (int)p1; + void *buf = (void *)p2; + size_t nbyte = (size_t)p3; + off64_t off = (off64_t)p4; + ssize_t ret; + int rval; + struct stat64 statbuf; + + if ((rval = fcntl(fd, F_GETFL, 0)) < 0) + return (-errno); + + if (!(rval & O_APPEND)) { + ret = pwrite64(fd, buf, nbyte, off); + } else if ((ret = fstat64(fd, &statbuf)) == 0) { + ret = pwrite64(fd, buf, nbyte, statbuf.st_size); + } + + if (ret < 0) + return (-errno); + + return (ret); +} + +#else /* 32 bit */ + +long +lx_pread64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5) +{ + int fd = (int)p1; + void *buf = (void *)p2; + size_t nbyte = (size_t)p3; + uintptr_t off_lo = p4; + uintptr_t off_hi = p5; + ssize_t ret; + + if (lx_is_directory(fd)) + return (-EISDIR); + + ret = pread64(fd, buf, nbyte, (off64_t)LX_32TO64(off_lo, off_hi)); + + if (ret < 0) + return (-errno); + + return (ret); +} + +/* + * On Linux, the pwrite(2) system call behaves identically to Solaris except + * in the case of the file being opened with O_APPEND. In that case Linux's + * pwrite(2) ignores the offset parameter and instead appends the data to the + * file without modifying the current seek pointer. + */ +long +lx_pwrite64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5) +{ + int fd = (int)p1; + void *buf = (void *)p2; + size_t nbyte = (size_t)p3; + uintptr_t off_lo = p4; + uintptr_t off_hi = p5; + ssize_t ret; + int rval; + struct stat64 statbuf; + + if ((rval = fcntl(fd, F_GETFL, 0)) < 0) + return (-errno); + + if (!(rval & O_APPEND)) { + ret = pwrite64(fd, buf, nbyte, + (off64_t)LX_32TO64(off_lo, off_hi)); + } else if ((ret = fstat64(fd, &statbuf)) == 0) { + ret = pwrite64(fd, buf, nbyte, statbuf.st_size); + } + + if (ret < 0) + return (-errno); + + return (ret); +} +#endif + +/* + * Implementation of Linux readv() and writev() system calls. + * + * The Linux system calls differ from the Solaris system calls in a few key + * areas: + * + * - On Solaris, the maximum number of I/O vectors that can be passed to readv() + * or writev() is IOV_MAX (16). Linux has a much larger restriction (1024). + * + * - Passing 0 as a vector count is an error on Solaris, but on Linux results + * in a return value of 0. Even though the man page says the opposite. + * + * - If the Nth vector results in an error, Solaris will return an error code + * for the entire operation. Linux only returns an error if there has been + * no data transferred yet. Otherwise, it returns the number of bytes + * transferred up until that point. + * + * In order to accomodate these differences, we implement these functions as a + * series of ordinary read() or write() calls. + */ + +#define LX_IOV_MAX 1024 /* Also called MAX_IOVEC */ + +static int +lx_iovec_copy_and_check(const struct iovec *iovp, struct iovec *iov, int count) +{ +#if defined(_ILP32) + int i; + ssize_t cnt = 0; +#endif + + if (uucopy(iovp, (void *)iov, count * sizeof (struct iovec)) != 0) + return (-errno); + +#if defined(_ILP32) + for (i = 0; i < count; i++) { + cnt += iov[i].iov_len; + if (iov[i].iov_len < 0 || cnt < 0) + return (-EINVAL); + } +#endif + + return (0); +} + +long +lx_readv(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int fd = (int)p1; + const struct iovec *iovp = (const struct iovec *)p2; + int count = (int)p3; + struct iovec *iov; + ssize_t total = 0, ret; + int i; + + if (count == 0) + return (0); + + if (count < 0 || count > LX_IOV_MAX) + return (-EINVAL); + + if (lx_is_directory(fd)) + return (-EISDIR); + + iov = SAFE_ALLOCA(count * sizeof (struct iovec)); + if (iov == NULL) + return (-ENOMEM); + if ((ret = lx_iovec_copy_and_check(iovp, iov, count)) != 0) + return (ret); + + for (i = 0; i < count; i++) { + ret = read(fd, iov[i].iov_base, iov[i].iov_len); + + if (ret < 0) { + if (total > 0) + return (total); + return (-errno); + } + + total += ret; + } + + return (total); +} + +long +lx_writev(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int fd = (int)p1; + const struct iovec *iovp = (const struct iovec *)p2; + int count = (int)p3; + struct iovec *iov; + ssize_t total = 0, ret; + int i; + + if (count == 0) + return (0); + + if (count < 0 || count > LX_IOV_MAX) + return (-EINVAL); + + iov = SAFE_ALLOCA(count * sizeof (struct iovec)); + if (iov == NULL) + return (-ENOMEM); + if ((ret = lx_iovec_copy_and_check(iovp, iov, count)) != 0) + return (ret); + + for (i = 0; i < count; i++) { + ret = write(fd, iov[i].iov_base, iov[i].iov_len); + + if (ret < 0) { + if (total > 0) + return (total); + return (-errno); + } + + total += ret; + } + + return (total); +} + +long +lx_preadv(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int fd = (int)p1; + const struct iovec *iovp = (const struct iovec *)p2; + int cnt = (int)p3; + off_t off = (off_t)p4; + ssize_t ret; + + ret = preadv(fd, iovp, cnt, off); + return (ret < 0 ? -errno : ret); +} + +long +lx_pwritev(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int fd = (int)p1; + const struct iovec *iovp = (const struct iovec *)p2; + int cnt = (int)p3; + off_t off = (off_t)p4; + ssize_t ret; + + ret = pwritev(fd, iovp, cnt, off); + return (ret < 0 ? -errno : 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..21da0771fc --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c @@ -0,0 +1,1608 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#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_stat.h> +#include <sys/lx_statfs.h> +#include <sys/lx_signal.h> +#include <sys/lx_syscall.h> +#include <sys/lx_thread.h> +#include <sys/lx_aio.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_VERS_MAX]; +char lx_cmd_name[MAXNAMLEN]; + +/* + * 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_bottom; + +#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; + +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. + */ +int +lx_check_alloca(size_t sz) +{ + uintptr_t sp = (uintptr_t)&sz; + uintptr_t end = sp - sz; + + return ((end < sp) && (end >= stack_bottom)); +} + +/*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); + /* LINTED [possible expansion issues] */ + (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); + +#if defined(_LP64) +static void * +map_vdso() +{ + int fd; + mmapobj_result_t mpp[10]; /* we know the size of our lib */ + mmapobj_result_t *smpp = mpp; + uint_t mapnum = 10; + + if ((fd = open("/native/usr/lib/brand/lx/amd64/lx_vdso.so.1", + O_RDONLY)) == -1) + lx_err_fatal("couldn't open lx_vdso.so.1"); + + if (mmapobj(fd, MMOBJ_INTERPRET, smpp, &mapnum, NULL) == -1) + lx_err_fatal("couldn't mmapobj lx_vdso.so.1"); + + (void) close(fd); + + /* assume first segment is the base of the mapping */ + return (smpp->mr_addr); +} +#endif + +/* + * 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)); + } +} + +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) = NULL; + LX_REG(&jump_uc, REG_SP) = sp; + LX_REG(&jump_uc, REG_PC) = entry; + +#if defined(_LP64) + /* + * The AMD64 ABI states that at process entry, %rdx contains "a + * function pointer that the application should register with + * atexit()". We make sure to pass NULL explicitly so that + * no function is registered. + */ + LX_REG(&jump_uc, REG_RDX) = NULL; +#endif + + lx_debug("starting Linux program sp %p ldentry %p", sp, entry); + + /* + * This system call should not return. + */ + if (syscall(SYS_brand, B_JUMP_TO_LINUX, &jump_uc) == -1) { + lx_err_fatal("B_JUMP_TO_LINUX failed: %s", + strerror(errno)); + } + abort(); +} + +/*ARGSUSED*/ +int +lx_init(int argc, char *argv[], char *envp[]) +{ + char *r; + auxv_t *ap; + long *p; + int err; + lx_elf_data_t edp; + lx_brand_registration_t reg; + lx_tsd_t *lxtsd; +#if defined(_LP64) + void *vdso_hdr; +#endif + + bzero(®, sizeof (reg)); + + stack_bottom = 2 * sysconf(_SC_PAGESIZE); + + /* + * 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); + + lx_debug_init(); + + r = getenv("LX_RELEASE"); + if (r == NULL) { + if (zone_getattr(getzoneid(), LX_KERN_VERSION_NUM, lx_release, + sizeof (lx_release)) != sizeof (lx_release)) + (void) strlcpy(lx_release, "2.4.21", LX_VERS_MAX); + } else { + (void) strlcpy(lx_release, r, 128); + } + + lx_debug("lx_release: %s\n", lx_release); + + /* + * Should we kill an application that attempts an unimplemented + * system call? + */ + if (getenv("LX_STRICT") != NULL) { + reg.lxbr_flags |= LX_PROC_STRICT_MODE; + lx_debug("STRICT mode enabled.\n"); + } + + /* + * Are we in install mode? + */ + if (getenv("LX_INSTALL") != NULL) { + reg.lxbr_flags |= LX_PROC_INSTALL_MODE; + lx_install = 1; + lx_debug("INSTALL mode enabled.\n"); + } + + /* + * Should we attempt to send messages to the screen? + */ + if (getenv("LX_VERBOSE") != NULL) { + lx_verbose = 1; + lx_debug("VERBOSE 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_stat_init() != 0) + lx_err_fatal("failed to setup the stat translator"); + + if (lx_statfs_init() != 0) + lx_err_fatal("failed to setup the statfs translator"); + + lx_ptrace_init(); + +#if defined(_LP64) + vdso_hdr = map_vdso(); +#endif + + /* + * Find the aux vector on the stack. + */ + p = (long *)envp; + while (*p != NULL) + p++; + /* + * p is now pointing at the 0 word after the environ pointers. After + * that is the aux vectors. + */ + p++; + for (ap = (auxv_t *)p; ap->a_type != 0; ap++) { + switch (ap->a_type) { + case AT_BASE: + ap->a_un.a_val = edp.ed_base; + break; + case AT_ENTRY: + ap->a_un.a_val = edp.ed_entry; + break; + case AT_PHDR: + ap->a_un.a_val = edp.ed_phdr; + break; + case AT_PHENT: + ap->a_un.a_val = edp.ed_phent; + break; + case AT_PHNUM: + ap->a_un.a_val = edp.ed_phnum; + break; +#if defined(_LP64) + case AT_SUN_BRAND_LX_SYSINFO_EHDR: + ap->a_type = AT_SYSINFO_EHDR; + ap->a_un.a_val = (long)vdso_hdr; + break; +#endif + default: + break; + } + } + + /* 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)\n", ev); + + /* + * 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 */ + lx_close, /* 3: close */ + lx_stat64, /* 4: stat */ + lx_fstat64, /* 5: fstat */ + lx_lstat64, /* 6: lstat */ + lx_poll, /* 7: poll */ + lx_lseek, /* 8: lseek */ + lx_mmap, /* 9: mmap */ + lx_mprotect, /* 10: mprotect */ + lx_munmap, /* 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 */ + lx_pread, /* 17: pread64 */ + lx_pwrite, /* 18: pwrite64 */ + lx_readv, /* 19: readv */ + lx_writev, /* 20: writev */ + lx_access, /* 21: access */ + NULL, /* 22: pipe */ + lx_select, /* 23: select */ + NULL, /* 24: sched_yield */ + lx_remap, /* 25: mremap */ + lx_msync, /* 26: msync */ + lx_mincore, /* 27: mincore */ + lx_madvise, /* 28: madvise */ + lx_shmget, /* 29: shmget */ + lx_shmat, /* 30: shmat */ + lx_shmctl, /* 31: shmctl */ + lx_dup, /* 32: dup */ + lx_dup2, /* 33: dup2 */ + lx_pause, /* 34: pause */ + lx_nanosleep, /* 35: nanosleep */ + lx_getitimer, /* 36: getitimer */ + lx_alarm, /* 37: alarm */ + lx_setitimer, /* 38: setitimer */ + NULL, /* 39: getpid */ + lx_sendfile64, /* 40: sendfile */ + lx_socket, /* 41: socket */ + lx_connect, /* 42: connect */ + lx_accept, /* 43: accept */ + lx_sendto, /* 44: sendto */ + lx_recvfrom, /* 45: recvfrom */ + lx_sendmsg, /* 46: sendmsg */ + lx_recvmsg, /* 47: recvmsg */ + lx_shutdown, /* 48: shutdown */ + lx_bind, /* 49: bind */ + lx_listen, /* 50: listen */ + lx_getsockname, /* 51: getsockname */ + lx_getpeername, /* 52: getpeername */ + lx_socketpair, /* 53: socketpair */ + lx_setsockopt, /* 54: setsockopt */ + lx_getsockopt, /* 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 */ + lx_uname, /* 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 */ + lx_flock, /* 73: flock */ + lx_fsync, /* 74: fsync */ + lx_fdatasync, /* 75: fdatasync */ + lx_truncate, /* 76: truncate */ + lx_ftruncate, /* 77: ftruncate */ + NULL, /* 78: getdents */ + lx_getcwd, /* 79: getcwd */ + lx_chdir, /* 80: chdir */ + lx_fchdir, /* 81: fchdir */ + lx_rename, /* 82: rename */ + NULL, /* 83: mkdir */ + lx_rmdir, /* 84: rmdir */ + lx_creat, /* 85: creat */ + lx_link, /* 86: link */ + lx_unlink, /* 87: unlink */ + lx_symlink, /* 88: symlink */ + lx_readlink, /* 89: readlink */ + NULL, /* 90: chmod */ + NULL, /* 91: fchmod */ + NULL, /* 92: chown */ + NULL, /* 93: fchown */ + NULL, /* 94: lchown */ + lx_umask, /* 95: umask */ + NULL, /* 96: gettimeofday */ + NULL, /* 97: getrlimit */ + lx_getrusage, /* 98: getrusage */ + NULL, /* 99: sysinfo */ + lx_times, /* 100: times */ + lx_ptrace, /* 101: ptrace */ + lx_getuid, /* 102: getuid */ + lx_syslog, /* 103: syslog */ + lx_getgid, /* 104: getgid */ + lx_setuid, /* 105: setuid */ + lx_setgid, /* 106: setgid */ + lx_geteuid, /* 107: geteuid */ + lx_getegid, /* 108: getegid */ + lx_setpgid, /* 109: setpgid */ + NULL, /* 110: getppid */ + lx_getpgrp, /* 111: getpgrp */ + lx_setsid, /* 112: setsid */ + lx_setreuid, /* 113: setreuid */ + lx_setregid, /* 114: setregid */ + lx_getgroups, /* 115: getgroups */ + lx_setgroups, /* 116: setgroups */ + NULL, /* 117: setresuid */ + lx_getresuid, /* 118: getresuid */ + NULL, /* 119: setresgid */ + lx_getresgid, /* 120: getresgid */ + lx_getpgid, /* 121: getpgid */ + lx_setfsuid, /* 122: setfsuid */ + lx_setfsgid, /* 123: setfsgid */ + lx_getsid, /* 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 */ + lx_personality, /* 135: personality */ + NULL, /* 136: ustat */ + lx_statfs, /* 137: statfs */ + lx_fstatfs, /* 138: fstatfs */ + lx_sysfs, /* 139: sysfs */ + lx_getpriority, /* 140: getpriority */ + lx_setpriority, /* 141: setpriority */ + lx_sched_setparam, /* 142: sched_setparam */ + lx_sched_getparam, /* 143: sched_getparam */ + lx_sched_setscheduler, /* 144: sched_setscheduler */ + lx_sched_getscheduler, /* 145: sched_getscheduler */ + lx_sched_get_priority_max, /* 146: sched_get_priority_max */ + lx_sched_get_priority_min, /* 147: sched_get_priority_min */ + lx_sched_rr_get_interval, /* 148: sched_rr_get_interval */ + lx_mlock, /* 149: mlock */ + lx_munlock, /* 150: munlock */ + lx_mlockall, /* 151: mlockall */ + lx_munlockall, /* 152: munlockall */ + lx_vhangup, /* 153: vhangup */ + NULL, /* 154: modify_ldt */ + NULL, /* 155: pivot_root */ + lx_sysctl, /* 156: sysctl */ + lx_prctl, /* 157: prctl */ + NULL, /* 158: arch_prctl */ + lx_adjtimex, /* 159: adjtimex */ + NULL, /* 160: setrlimit */ + lx_chroot, /* 161: chroot */ + lx_sync, /* 162: sync */ + NULL, /* 163: acct */ + lx_settimeofday, /* 164: settimeofday */ + lx_mount, /* 165: mount */ + lx_umount2, /* 166: umount2 */ + NULL, /* 167: swapon */ + NULL, /* 168: swapoff */ + lx_reboot, /* 169: reboot */ + lx_sethostname, /* 170: sethostname */ + lx_setdomainname, /* 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 */ + lx_sched_setaffinity, /* 203: sched_setaffinity */ + lx_sched_getaffinity, /* 204: sched_getaffinity */ + NULL, /* 205: set_thread_area */ + lx_io_setup, /* 206: io_setup */ + lx_io_destroy, /* 207: io_destroy */ + lx_io_getevents, /* 208: io_getevents */ + lx_io_submit, /* 209: io_submit */ + lx_io_cancel, /* 210: io_cancel */ + NULL, /* 211: get_thread_area */ + NULL, /* 212: lookup_dcookie */ + lx_epoll_create, /* 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 */ + lx_fadvise64_64, /* 221: fadvise64 */ + lx_timer_create, /* 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 */ + lx_epoll_wait, /* 232: epoll_wait */ + lx_epoll_ctl, /* 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 */ + lx_fstatat64, /* 262: fstatat64 */ + lx_unlinkat, /* 263: unlinkat */ + lx_renameat, /* 264: renameat */ + lx_linkat, /* 265: linkat */ + lx_symlinkat, /* 266: symlinkat */ + lx_readlinkat, /* 267: readlinkat */ + NULL, /* 268: fchmodat */ + lx_faccessat, /* 269: faccessat */ + lx_pselect6, /* 270: pselect6 */ + lx_ppoll, /* 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 */ + lx_epoll_pwait, /* 281: epoll_pwait */ + NULL, /* 282: signalfd */ + lx_timerfd_create, /* 283: timerfd_create */ + lx_eventfd, /* 284: eventfd */ + NULL, /* 285: fallocate */ + lx_timerfd_settime, /* 286: timerfd_settime */ + lx_timerfd_gettime, /* 287: timerfd_gettime */ + lx_accept4, /* 288: accept4 */ + NULL, /* 289: signalfd4 */ + lx_eventfd2, /* 290: eventfd2 */ + lx_epoll_create1, /* 291: epoll_create1 */ + lx_dup3, /* 292: dup3 */ + NULL, /* 293: pipe2 */ + lx_inotify_init1, /* 294: inotify_init1 */ + lx_preadv, /* 295: preadv */ + lx_pwritev, /* 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 */ + lx_close, /* 6: close */ + NULL, /* 7: waitpid */ + lx_creat, /* 8: creat */ + lx_link, /* 9: link */ + lx_unlink, /* 10: unlink */ + lx_execve, /* 11: execve */ + lx_chdir, /* 12: chdir */ + NULL, /* 13: time */ + lx_mknod, /* 14: mknod */ + NULL, /* 15: chmod */ + NULL, /* 16: lchown16 */ + NULL, /* 17: break */ + NULL, /* 18: stat */ + lx_lseek, /* 19: lseek */ + NULL, /* 20: getpid */ + lx_mount, /* 21: mount */ + lx_umount, /* 22: umount */ + lx_setuid16, /* 23: setuid16 */ + lx_getuid16, /* 24: getuid16 */ + lx_stime, /* 25: stime */ + lx_ptrace, /* 26: ptrace */ + lx_alarm, /* 27: alarm */ + NULL, /* 28: fstat */ + lx_pause, /* 29: pause */ + lx_utime, /* 30: utime */ + NULL, /* 31: stty */ + NULL, /* 32: gtty */ + lx_access, /* 33: access */ + lx_nice, /* 34: nice */ + NULL, /* 35: ftime */ + lx_sync, /* 36: sync */ + NULL, /* 37: kill */ + lx_rename, /* 38: rename */ + NULL, /* 39: mkdir */ + lx_rmdir, /* 40: rmdir */ + lx_dup, /* 41: dup */ + NULL, /* 42: pipe */ + lx_times, /* 43: times */ + NULL, /* 44: prof */ + NULL, /* 45: brk */ + lx_setgid16, /* 46: setgid16 */ + lx_getgid16, /* 47: getgid16 */ + lx_signal, /* 48: signal */ + lx_geteuid16, /* 49: geteuid16 */ + lx_getegid16, /* 50: getegid16 */ + NULL, /* 51: acct */ + lx_umount2, /* 52: umount2 */ + NULL, /* 53: lock */ + NULL, /* 54: ioctl */ + NULL, /* 55: fcntl */ + NULL, /* 56: mpx */ + lx_setpgid, /* 57: setpgid */ + NULL, /* 58: ulimit */ + NULL, /* 59: olduname */ + lx_umask, /* 60: umask */ + lx_chroot, /* 61: chroot */ + NULL, /* 62: ustat */ + lx_dup2, /* 63: dup2 */ + NULL, /* 64: getppid */ + lx_getpgrp, /* 65: getpgrp */ + lx_setsid, /* 66: setsid */ + lx_sigaction, /* 67: sigaction */ + NULL, /* 68: sgetmask */ + NULL, /* 69: ssetmask */ + lx_setreuid16, /* 70: setreuid16 */ + lx_setregid16, /* 71: setregid16 */ + lx_sigsuspend, /* 72: sigsuspend */ + lx_sigpending, /* 73: sigpending */ + lx_sethostname, /* 74: sethostname */ + NULL, /* 75: setrlimit */ + NULL, /* 76: getrlimit */ + lx_getrusage, /* 77: getrusage */ + NULL, /* 78: gettimeofday */ + lx_settimeofday, /* 79: settimeofday */ + lx_getgroups16, /* 80: getgroups16 */ + lx_setgroups16, /* 81: setgroups16 */ + NULL, /* 82: select */ + lx_symlink, /* 83: symlink */ + NULL, /* 84: oldlstat */ + lx_readlink, /* 85: readlink */ + NULL, /* 86: uselib */ + NULL, /* 87: swapon */ + lx_reboot, /* 88: reboot */ + lx_readdir, /* 89: readdir */ + lx_mmap, /* 90: mmap */ + lx_munmap, /* 91: munmap */ + lx_truncate, /* 92: truncate */ + lx_ftruncate, /* 93: ftruncate */ + NULL, /* 94: fchmod */ + NULL, /* 95: fchown16 */ + lx_getpriority, /* 96: getpriority */ + lx_setpriority, /* 97: setpriority */ + NULL, /* 98: profil */ + lx_statfs, /* 99: statfs */ + lx_fstatfs, /* 100: fstatfs */ + NULL, /* 101: ioperm */ + lx_socketcall, /* 102: socketcall */ + lx_syslog, /* 103: syslog */ + lx_setitimer, /* 104: setitimer */ + lx_getitimer, /* 105: getitimer */ + lx_stat, /* 106: stat */ + lx_lstat, /* 107: lstat */ + lx_fstat, /* 108: fstat */ + NULL, /* 109: uname */ + NULL, /* 110: oldiopl */ + lx_vhangup, /* 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 */ + lx_setdomainname, /* 121: setdomainname */ + lx_uname, /* 122: uname */ + NULL, /* 123: modify_ldt */ + lx_adjtimex, /* 124: adjtimex */ + lx_mprotect, /* 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 */ + lx_getpgid, /* 132: getpgid */ + lx_fchdir, /* 133: fchdir */ + NULL, /* 134: bdflush */ + lx_sysfs, /* 135: sysfs */ + lx_personality, /* 136: personality */ + NULL, /* 137: afs_syscall */ + lx_setfsuid16, /* 138: setfsuid16 */ + lx_setfsgid16, /* 139: setfsgid16 */ + lx_llseek, /* 140: llseek */ + NULL, /* 141: getdents */ + lx_select, /* 142: select */ + lx_flock, /* 143: flock */ + lx_msync, /* 144: msync */ + lx_readv, /* 145: readv */ + lx_writev, /* 146: writev */ + lx_getsid, /* 147: getsid */ + lx_fdatasync, /* 148: fdatasync */ + lx_sysctl, /* 149: sysctl */ + lx_mlock, /* 150: mlock */ + lx_munlock, /* 151: munlock */ + lx_mlockall, /* 152: mlockall */ + lx_munlockall, /* 153: munlockall */ + lx_sched_setparam, /* 154: sched_setparam */ + lx_sched_getparam, /* 155: sched_getparam */ + lx_sched_setscheduler, /* 156: sched_setscheduler */ + lx_sched_getscheduler, /* 157: sched_getscheduler */ + NULL, /* 158: sched_yield */ + lx_sched_get_priority_max, /* 159: sched_get_priority_max */ + lx_sched_get_priority_min, /* 160: sched_get_priority_min */ + lx_sched_rr_get_interval, /* 161: sched_rr_get_interval */ + lx_nanosleep, /* 162: nanosleep */ + lx_remap, /* 163: mremap */ + NULL, /* 164: setresuid16 */ + lx_getresuid16, /* 165: getresuid16 */ + NULL, /* 166: vm86 */ + lx_query_module, /* 167: query_module */ + lx_poll, /* 168: poll */ + NULL, /* 169: nfsservctl */ + NULL, /* 170: setresgid16 */ + lx_getresgid16, /* 171: getresgid16 */ + lx_prctl, /* 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 */ + lx_pread64, /* 180: pread64 */ + lx_pwrite64, /* 181: pwrite64 */ + NULL, /* 182: chown16 */ + lx_getcwd, /* 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 */ + lx_mmap2, /* 192: mmap2 */ + lx_truncate64, /* 193: truncate64 */ + lx_ftruncate64, /* 194: ftruncate64 */ + lx_stat64, /* 195: stat64 */ + lx_lstat64, /* 196: lstat64 */ + lx_fstat64, /* 197: fstat64 */ + NULL, /* 198: lchown */ + lx_getuid, /* 199: getuid */ + lx_getgid, /* 200: getgid */ + lx_geteuid, /* 201: geteuid */ + lx_getegid, /* 202: getegid */ + lx_setreuid, /* 203: setreuid */ + lx_setregid, /* 204: setregid */ + lx_getgroups, /* 205: getgroups */ + lx_setgroups, /* 206: setgroups */ + NULL, /* 207: fchown */ + NULL, /* 208: setresuid */ + lx_getresuid, /* 209: getresuid */ + NULL, /* 210: setresgid */ + lx_getresgid, /* 211: getresgid */ + NULL, /* 212: chown */ + lx_setuid, /* 213: setuid */ + lx_setgid, /* 214: setgid */ + lx_setfsuid, /* 215: setfsuid */ + lx_setfsgid, /* 216: setfsgid */ + NULL, /* 217: pivot_root */ + lx_mincore, /* 218: mincore */ + lx_madvise, /* 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 */ + lx_sched_setaffinity, /* 241: sched_setaffinity */ + lx_sched_getaffinity, /* 242: sched_getaffinity */ + NULL, /* 243: set_thread_area */ + NULL, /* 244: get_thread_area */ + lx_io_setup, /* 245: io_setup */ + lx_io_destroy, /* 246: io_destroy */ + lx_io_getevents, /* 247: io_getevents */ + lx_io_submit, /* 248: io_submit */ + lx_io_cancel, /* 249: io_cancel */ + lx_fadvise64, /* 250: fadvise64 */ + NULL, /* 251: nosys */ + lx_group_exit, /* 252: group_exit */ + NULL, /* 253: lookup_dcookie */ + lx_epoll_create, /* 254: epoll_create */ + lx_epoll_ctl, /* 255: epoll_ctl */ + lx_epoll_wait, /* 256: epoll_wait */ + NULL, /* 257: remap_file_pages */ + NULL, /* 258: set_tid_address */ + lx_timer_create, /* 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 */ + lx_fadvise64_64, /* 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 */ + lx_fstatat64, /* 300: fstatat64 */ + lx_unlinkat, /* 301: unlinkat */ + lx_renameat, /* 302: renameat */ + lx_linkat, /* 303: linkat */ + lx_symlinkat, /* 304: symlinkat */ + lx_readlinkat, /* 305: readlinkat */ + NULL, /* 306: fchmodat */ + lx_faccessat, /* 307: faccessat */ + lx_pselect6, /* 308: pselect6 */ + lx_ppoll, /* 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 */ + lx_epoll_pwait, /* 319: epoll_pwait */ + lx_utimensat, /* 320: utimensat */ + NULL, /* 321: signalfd */ + lx_timerfd_create, /* 322: timerfd_create */ + lx_eventfd, /* 323: eventfd */ + NULL, /* 324: fallocate */ + lx_timerfd_settime, /* 325: timerfd_settime */ + lx_timerfd_gettime, /* 326: timerfd_gettime */ + NULL, /* 327: signalfd4 */ + lx_eventfd2, /* 328: eventfd2 */ + lx_epoll_create1, /* 329: epoll_create1 */ + lx_dup3, /* 330: dup3 */ + NULL, /* 331: pipe2 */ + lx_inotify_init1, /* 332: inotify_init1 */ + lx_preadv, /* 333: preadv */ + lx_pwritev, /* 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 b/usr/src/lib/brand/lx/lx_brand/common/mapfile new file mode 100644 index 0000000000..0663f4bc19 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mapfile @@ -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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# 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 +# + +# +# Scope everything local -- our .init section is our only public interface. +# +{ + local: + *; +}; 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..0663f4bc19 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers @@ -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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# 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 +# + +# +# Scope everything local -- our .init section is our only public interface. +# +{ + local: + *; +}; diff --git a/usr/src/lib/brand/lx/lx_brand/common/mem.c b/usr/src/lib/brand/lx/lx_brand/common/mem.c new file mode 100644 index 0000000000..1e50f3def9 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mem.c @@ -0,0 +1,486 @@ +/* + * 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 <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <procfs.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +/* + * There are two forms of mmap, mmap() and mmap2(). The only difference is that + * the final argument to mmap2() specifies the number of pages, not bytes. + * Linux has a number of additional flags, but they are all deprecated. We also + * ignore the MAP_GROWSDOWN flag, which has no equivalent on Solaris. + * + * The Linux mmap() returns ENOMEM in some cases where Solaris returns + * EOVERFLOW, so we translate the errno as necessary. + */ + +int pagesize; /* needed for mmap2() */ + +#define LX_MAP_ANONYMOUS 0x00020 +#define LX_MAP_LOCKED 0x02000 +#define LX_MAP_NORESERVE 0x04000 +#define LX_MAP_32BIT 0x00040 + +#define LX_MADV_REMOVE 9 +#define LX_MADV_DONTFORK 10 +#define LX_MADV_DOFORK 11 +#define LX_MADV_MERGEABLE 12 +#define LX_MADV_UNMERGEABLE 13 +#define LX_MADV_HUGEPAGE 14 +#define LX_MADV_NOHUGEPAGE 15 +#define LX_MADV_DONTDUMP 16 +#define LX_MADV_DODUMP 17 + +static int +ltos_mmap_flags(int flags) +{ + int new_flags; + + new_flags = flags & (MAP_TYPE | MAP_FIXED); + + if (flags & LX_MAP_ANONYMOUS) + new_flags |= MAP_ANONYMOUS; + if (flags & LX_MAP_NORESERVE) + new_flags |= MAP_NORESERVE; + +#if defined(_LP64) + if (flags & LX_MAP_32BIT) + new_flags |= MAP_32BIT; +#endif + + return (new_flags); +} + +static void * +mmap_common(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5, off64_t p6) +{ + void *addr = (void *)p1; + size_t len = p2; + int prot = p3; + int flags = p4; + int fd = p5; + off64_t off = p6; + void *ret; + + if (LX_DEBUG_ISENABLED) { + char *path, path_buf[MAXPATHLEN]; + + path = lx_fd_to_path(fd, path_buf, sizeof (path_buf)); + if (path == NULL) + path = "?"; + + lx_debug("\tmmap_common(): fd = %d - %s", fd, path); + } + + /* + * Under Linux, the file descriptor is ignored when mapping zfod + * anonymous memory, On Solaris, we want the fd set to -1 for the + * same functionality. + */ + if (flags & LX_MAP_ANONYMOUS) + fd = -1; + + /* + * We refuse, as a matter of principle, to overcommit memory. + * Unfortunately, several bits of important and popular software expect + * to be able to pre-allocate large amounts of virtual memory but then + * probably never use it. One particularly bad example of this + * practice is golang. + * + * In the interest of running software, unsafe or not, we fudge + * something vaguely similar to overcommit by permanently enabling + * MAP_NORESERVE unless MAP_LOCKED was requested: + */ + if (!(flags & LX_MAP_LOCKED)) { + flags |= LX_MAP_NORESERVE; + } + + /* + * This is totally insane. The NOTES section in the linux mmap(2) man + * page claims that on some architectures, read protection may + * automatically include exec protection. It has been observed on a + * native linux system that the /proc/<pid>/maps file does indeed + * show that segments mmap'd from userland (such as libraries mapped in + * by the dynamic linker) all have exec the permission set, even for + * data segments. + */ + if (prot & PROT_READ) + prot |= PROT_EXEC; + + ret = mmap64(addr, len, prot, ltos_mmap_flags(flags), fd, off); + + if (ret == MAP_FAILED) + return ((void *)(long)(errno == EOVERFLOW ? -ENOMEM : -errno)); + + if (flags & LX_MAP_LOCKED) + (void) mlock(ret, len); + + return (ret); +} + +long +lx_mmap(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5, uintptr_t p6) +{ + return ((ssize_t)mmap_common(p1, p2, p3, p4, p5, (off64_t)p6)); +} + +long +lx_mmap2(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5, uintptr_t p6) +{ + if (pagesize == 0) + pagesize = sysconf(_SC_PAGESIZE); + + return ((ssize_t)mmap_common(p1, p2, p3, p4, p5, + (off64_t)p6 * pagesize)); +} + + +/* + * The locking family of system calls, as well as msync(), are identical. On + * Solaris, they are layered on top of the memcntl syscall, so they cannot be + * pass-thru. + */ +long +lx_mlock(uintptr_t addr, uintptr_t len) +{ + uintptr_t addr1 = addr & PAGEMASK; + uintptr_t len1 = len + (addr & PAGEOFFSET); + + return (mlock((void *)addr1, (size_t)len1) ? -errno : 0); +} + +long +lx_mlockall(uintptr_t flags) +{ + return (mlockall(flags) ? -errno : 0); +} + +long +lx_munlock(uintptr_t addr, uintptr_t len) +{ + uintptr_t addr1 = addr & PAGEMASK; + uintptr_t len1 = len + (addr & PAGEOFFSET); + + return (munlock((void *)addr1, (size_t)len1) ? -errno : 0); +} + +long +lx_munlockall(void) +{ + return (munlockall() ? -errno : 0); +} + +long +lx_msync(uintptr_t addr, uintptr_t len, uintptr_t flags) +{ + return (msync((void *)addr, (size_t)len, flags) ? -errno : 0); +} + +/* + * Illumos and Linux overlap on the basic flags, and are disjoint on the rest. + * Linux also allows the length to be zero, while Illumos does not. + */ +long +lx_madvise(uintptr_t start, uintptr_t len, uintptr_t advice) +{ + int ret; + + if (len == 0) + return (0); + + /* approximately similar */ + if (advice == LX_MADV_REMOVE) + advice = MADV_FREE; + + switch (advice) { + case MADV_NORMAL: + case MADV_RANDOM: + case MADV_SEQUENTIAL: + case MADV_WILLNEED: + case MADV_FREE: + case MADV_DONTNEED: + if (advice == MADV_DONTNEED) { + /* + * On Linux, MADV_DONTNEED implies an immediate purge + * of the specified region. This is spuriously + * different from (nearly) every other Unix, having + * apparently been done to mimic the semantics on + * Digital Unix (!). This is bad enough (MADV_FREE + * both has better semantics and results in better + * performance), but it gets worse: Linux applications + * (and notably, jemalloc) have managed to depend on + * the busted semantics of MADV_DONTNEED on Linux. We + * implement these semantics via MADV_PURGE -- and + * we translate our advice accordingly. + */ + advice = MADV_PURGE; + } + + ret = madvise((void *)start, len, advice); + if (ret == -1) { + if (errno == EBUSY) { + if (advice != MADV_PURGE) + return (-EINVAL); + + /* + * If we got an EBUSY from a MADV_PURGE, we + * will now try again with a MADV_DONTNEED: + * there are conditions (namely, with locked + * mappings that haven't yet been faulted in) + * where MADV_PURGE will fail but MADV_DONTNEED + * will succeed. If this succeeds, we'll call + * the operation successful; if not, we'll kick + * back EINVAL. + */ + advice = MADV_DONTNEED; + + if (madvise((void *)start, len, advice) == 0) + return (0); + + return (-EINVAL); + } + + return (-errno); + } else { + return (0); + } + + /* harmless to pretend these work */ + case LX_MADV_DONTFORK: + case LX_MADV_DOFORK: + case LX_MADV_HUGEPAGE: + case LX_MADV_NOHUGEPAGE: + case LX_MADV_DONTDUMP: + case LX_MADV_DODUMP: + return (0); + + /* we'll return an error for the rest of the Linux flags */ + default: + return (-EINVAL); + } +} + +/* + * mprotect() is identical except that we ignore the Linux flags PROT_GROWSDOWN + * and PROT_GROWSUP, which have no equivalent on Solaris. + */ +#define LX_PROT_GROWSDOWN 0x01000000 +#define LX_PROT_GROWSUP 0x02000000 + +long +lx_mprotect(uintptr_t start, uintptr_t len, uintptr_t prot) +{ + prot &= ~(LX_PROT_GROWSUP | LX_PROT_GROWSDOWN); + + return (mprotect((void *)start, len, prot) ? -errno : 0); +} + +#define LX_MREMAP_MAYMOVE 1 /* mapping can be moved */ +#define LX_MREMAP_FIXED 2 /* address is fixed */ + +/* + * We don't have a native mremap() (and nor do we particularly want one), so + * we emulate it strictly in user-land. The idea is simple: we just want to + * mmap() the underlying object with the new size and rip down the old mapping. + * However, this is problematic because we don't actually have the file + * descriptor that corresponds to the resized mapping (and indeed, the mapped + * file may not exist in any file system name space). So to get a file + * descriptor, we find the (or rather, a) path to the mapped object via its + * entry in /proc/self/path and attempt to open it. Assuming that this + * succeeds, we then mmap() it and rip down the original mapping. There are + * clearly many reasons why this might fail; absent a more apt errno (e.g., + * ENOMEM in some cases), we return EINVAL to denote these cases. + */ +long +lx_remap(uintptr_t old_address, uintptr_t old_size, + uintptr_t new_size, uintptr_t flags, uintptr_t new_address) +{ + int prot = 0, oflags, mflags = 0, len, fd = -1, i, nmap; + prmap_t map, *maps; + uintptr_t rval; + char path[256], buf[MAXPATHLEN + 1]; + struct stat st; + ssize_t n; + boolean_t found = B_FALSE; + + /* + * We need to search the mappings to find our specified mapping. Note + * that to perform this search, we use /proc/self/rmap instead of + * /proc/self/map. This is to accommodate the case where an mmap()'d + * and then ftruncate()'d file is being mremap()'d: rmap will report + * the size of the mapping (which we need to validate old_size) where + * map will report the smaller of the size of the mapping and the + * size of the object. (The "r" in "rmap" denotes "reserved".) + */ + if ((fd = open("/native/proc/self/rmap", O_RDONLY)) == -1 || + fstat(fd, &st) != 0) { + if (fd >= 0) { + (void) close(fd); + } + return (-EINVAL); + } + + /* + * Determine the number of mappings we need to read and allocate + * a buffer: + */ + nmap = st.st_size / sizeof (prmap_t); + if ((maps = malloc((nmap + 1) * sizeof (prmap_t))) == NULL) { + (void) close(fd); + return (-EINVAL); + } + + /* + * Read mappings from the kernel and determine how many complete + * mappings were read: + */ + if ((n = read(fd, maps, (nmap + 1) * sizeof (prmap_t))) < 0) { + lx_debug("\rread of /proc/self/map failed: %s", + strerror(errno)); + (void) close(fd); + free(maps); + return (-EINVAL); + } + nmap = n / sizeof (prmap_t); + lx_debug("\tfound %d mappings", nmap); + + /* + * Check if any mappings match our arguments: + */ + for (i = 0; i < nmap; i++) { + if (maps[i].pr_vaddr == old_address && + maps[i].pr_size == old_size) { + map = maps[i]; + found = B_TRUE; + break; + } + } + + (void) close(fd); + free(maps); + + if (!found) { + lx_debug("\tno matching mapping found"); + return (-EINVAL); + } + + if (!(map.pr_mflags & MA_SHARED)) { + /* + * If this is a private mapping, we're not going to remap it. + */ + return (-EINVAL); + } + + if (map.pr_mflags & (MA_ISM | MA_SHM)) { + /* + * If this is either ISM or System V shared memory, we're not + * going to remap it. + */ + return (-EINVAL); + } + + if (!(flags & LX_MREMAP_MAYMOVE)) { + /* + * If we're not allowed to move this mapping, we're going to + * act as if we can't expand it. + */ + return (-ENOMEM); + } + + oflags = (map.pr_mflags & MA_WRITE) ? O_RDWR : O_RDONLY; + + if (map.pr_mapname[0] == '\0') { + /* + * This is likely an anonymous mapping. + */ + return (-EINVAL); + } + + (void) snprintf(path, sizeof (path), + "/native/proc/self/path/%s", map.pr_mapname); + + if ((len = readlink(path, buf, sizeof (buf))) == -1 || + len == sizeof (buf)) { + /* + * If we failed to read the link, the path might not exist. + */ + return (-EINVAL); + } + buf[len] = '\0'; + + if ((fd = open(buf, oflags)) == -1) { + /* + * If we failed to open the object, it may be because it's + * not named (i.e., it's anonymous) or because we somehow + * don't have permissions. Either way, we're going to kick + * it back with EINVAL. + */ + return (-EINVAL); + } + + if (map.pr_mflags & MA_WRITE) + prot |= PROT_WRITE; + + if (map.pr_mflags & MA_READ) + prot |= PROT_READ; + + if (map.pr_mflags & MA_EXEC) + prot |= PROT_EXEC; + + mflags = MAP_SHARED; + + if (new_address != NULL && (flags & LX_MREMAP_FIXED)) { + mflags |= MAP_FIXED; + } else { + new_address = NULL; + } + + rval = (uintptr_t)mmap((void *)new_address, new_size, + prot, mflags, fd, map.pr_offset); + (void) close(fd); + + if ((void *)rval == MAP_FAILED) + return ((long)-ENOMEM); + + /* + * Our mapping succeeded; we're now going to rip down the old mapping. + */ + (void) munmap((void *)old_address, old_size); + + return (rval); +} 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..95234b819c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/misc.c @@ -0,0 +1,1120 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#include <stdlib.h> +#include <assert.h> +#include <alloca.h> +#include <errno.h> +#include <fcntl.h> +#include <strings.h> +#include <macros.h> +#include <sys/brand.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/epoll.h> +#include <sys/mman.h> +#include <sys/shm.h> +#include <sys/socket.h> +#include <sys/lx_types.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_stat.h> +#include <sys/lx_syscall.h> +#include <sys/lx_fcntl.h> +#include <sys/lx_thread.h> +#include <sys/inotify.h> +#include <sys/eventfd.h> +#include <sys/timerfd.h> +#include <thread.h> +#include <unistd.h> +#include <libintl.h> +#include <zone.h> +#include <priv.h> +#include <procfs.h> +#include <lx_syscall.h> + +extern int sethostname(char *, int); + +/* ARGUSED */ +long +lx_rename(uintptr_t p1, uintptr_t p2) +{ + int ret; + + ret = rename((const char *)p1, (const char *)p2); + + if (ret < 0) { + /* + * If rename(2) failed and we're in install mode, return + * success if the the reason we failed was either because the + * source file didn't actually exist or if it was because we + * tried to rename it to be the name of a device currently in + * use (resulting in an EBUSY.) + * + * To help install along further, if the failure was due + * to an EBUSY, delete the original file so we don't leave + * extra files lying around. + */ + if (lx_install != 0) { + if (errno == ENOENT) + return (0); + + if (errno == EBUSY) { + (void) unlink((const char *)p1); + return (0); + } + } + + return (-errno); + } + + return (0); +} + +long +lx_renameat(uintptr_t ext1, uintptr_t p1, uintptr_t ext2, uintptr_t p2) +{ + int ret; + int atfd1 = (int)ext1; + int atfd2 = (int)ext2; + + if (atfd1 == LX_AT_FDCWD) + atfd1 = AT_FDCWD; + + if (atfd2 == LX_AT_FDCWD) + atfd2 = AT_FDCWD; + + ret = renameat(atfd1, (const char *)p1, atfd2, (const char *)p2); + + if (ret < 0) { + /* see lx_rename() for why we check lx_install */ + if (lx_install != 0) { + if (errno == ENOENT) + return (0); + + if (errno == EBUSY) { + (void) unlinkat(ext1, (const char *)p1, 0); + return (0); + } + } + + return (-errno); + } + + return (0); +} + +/*ARGSUSED*/ +long +lx_reboot(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int magic = (int)p1; + int magic2 = (int)p2; + uint_t flag = (int)p3; + int rc; + + if (magic != LINUX_REBOOT_MAGIC1) + return (-EINVAL); + if (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && + magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C && + magic2 != LINUX_REBOOT_MAGIC2D) + return (-EINVAL); + + if (geteuid() != 0) + return (-EPERM); + + switch (flag) { + case LINUX_REBOOT_CMD_CAD_ON: + case LINUX_REBOOT_CMD_CAD_OFF: + /* ignored */ + rc = 0; + break; + case LINUX_REBOOT_CMD_POWER_OFF: + case LINUX_REBOOT_CMD_HALT: + rc = reboot(RB_HALT, NULL); + break; + case LINUX_REBOOT_CMD_RESTART: + case LINUX_REBOOT_CMD_RESTART2: + /* RESTART2 may need more work */ + lx_msg("Restarting system.\n"); + rc = reboot(RB_AUTOBOOT, NULL); + break; + default: + return (-EINVAL); + } + + return ((rc == -1) ? -errno : rc); +} + +/* + * getcwd() - Linux syscall semantics are slightly different; we need to return + * the length of the pathname copied (+ 1 for the terminating NULL byte.) + */ +long +lx_getcwd(uintptr_t p1, uintptr_t p2) +{ + char *buf; + size_t buflen = (size_t)p2; + size_t copylen, local_len; + size_t len = 0; + + if ((getcwd((char *)p1, (size_t)p2)) == NULL) + return (-errno); + + /* + * We need the length of the pathname getcwd() copied but we never want + * to dereference a Linux pointer for any reason. + * + * Thus, to get the string length we will uucopy() up to copylen + * bytes at a time into a local buffer and will walk each chunk looking + * for the string-terminating NULL byte. + * + * We can use strlen() to find the length of the string in the + * local buffer by delimiting the buffer with a NULL byte in the + * last element that will never be overwritten. + */ + copylen = min(buflen, MAXPATHLEN + 1); + buf = SAFE_ALLOCA(copylen + 1); + if (buf == NULL) + return (-ENOMEM); + buf[copylen] = '\0'; + + for (;;) { + if (uucopy((char *)p1 + len, buf, copylen) != 0) + return (-errno); + + local_len = strlen(buf); + len += local_len; + + /* + * If the strlen() is less than copylen, we found the + * real end of the string -- not the NULL byte used to + * delimit the end of our buffer. + */ + if (local_len != copylen) + break; + + /* prepare to check the next chunk of the string */ + buflen -= copylen; + copylen = min(buflen, copylen); + } + + return (len + 1); +} + +long +lx_uname(uintptr_t p1) +{ + struct lx_utsname *un = (struct lx_utsname *)p1; + char buf[LX_SYS_UTS_LN + 1]; + + if (gethostname(un->nodename, sizeof (un->nodename)) == -1) + return (-errno); + + (void) strlcpy(un->sysname, LX_UNAME_SYSNAME, LX_SYS_UTS_LN); + (void) strlcpy(un->release, lx_release, LX_SYS_UTS_LN); + (void) strlcpy(un->version, LX_UNAME_VERSION, LX_SYS_UTS_LN); + (void) strlcpy(un->machine, LX_UNAME_MACHINE, LX_SYS_UTS_LN); + if ((sysinfo(SI_SRPC_DOMAIN, buf, LX_SYS_UTS_LN) < 0)) + un->domainname[0] = '\0'; + else + (void) strlcpy(un->domainname, buf, LX_SYS_UTS_LN); + + return (0); +} + +/* + * {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); +} + +/* + * personality(). We don't really support Linux personalities, but we have to + * emulate enough (or ahem, lie) to show that we support the basic personality. + * We also allow certain (relatively) harmless bits of the personality to be + * "set" -- keeping track of whatever lie we're telling so we don't get caught + * out too easily. + */ +#define LX_PER_LINUX 0x0 +#define LX_PER_MASK 0xff + +/* + * These are for what Linux calls "bug emulation". + */ +#define LX_PER_UNAME26 0x0020000 +#define LX_PER_ADDR_NO_RANDOMIZE 0x0040000 +#define LX_PER_FDPIC_FUNCPTRS 0x0080000 +#define LX_PER_MMAP_PAGE_ZERO 0x0100000 +#define LX_PER_ADDR_COMPAT_LAYOUT 0x0200000 +#define LX_PER_READ_IMPLIES_EXEC 0x0400000 +#define LX_PER_ADDR_LIMIT_32BIT 0x0800000 +#define LX_PER_SHORT_INODE 0x1000000 +#define LX_PER_WHOLE_SECONDS 0x2000000 +#define LX_PER_STICKY_TIMEOUTS 0x4000000 +#define LX_PER_ADDR_LIMIT_3GB 0x8000000 + +long +lx_personality(uintptr_t p1) +{ + static int current = LX_PER_LINUX; + int per = (int)p1; + + switch (per) { + case -1: + /* Request current personality */ + return (current); + case LX_PER_LINUX: + current = per; + return (0); + default: + if (per & LX_PER_MASK) + return (-EINVAL); + + /* + * We allow a subset of the legacy emulation personality + * attributes to be "turned on" -- which we put in quotes + * because we don't actually change our behavior based on + * them. (Note that we silently ignore the others.) + */ + current = per & (LX_PER_ADDR_LIMIT_3GB | + LX_PER_ADDR_NO_RANDOMIZE | LX_PER_ADDR_COMPAT_LAYOUT); + + return (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_sethostname(uintptr_t p1, uintptr_t p2) +{ + char *name = (char *)p1; + int len = (size_t)p2; + + return (sethostname(name, len) ? -errno : 0); +} + +long +lx_setdomainname(uintptr_t p1, uintptr_t p2) +{ + char *name = (char *)p1; + int len = (size_t)p2; + long rval; + + if (len < 0 || len >= LX_SYS_UTS_LN) + return (-EINVAL); + + rval = sysinfo(SI_SET_SRPC_DOMAIN, name, len); + + return ((rval < 0) ? -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 }; + char path[64]; + + /* Get a copy of the executable we're trying to run */ + path[0] = '\0'; + (void) uucopystr(filename, path, sizeof (path)); + + /* Check if we're trying to run a native binary */ + if (strncmp(path, "/native/usr/lib/brand/lx/lx_native", + sizeof (path)) == 0) { + /* Skip the first element in the argv array */ + argv++; + + /* + * The name of the new program to execute was the first + * parameter passed to lx_native. + */ + if (uucopy(argv, &filename, sizeof (char *)) != 0) + return (-errno); + + (void) syscall(SYS_brand, B_EXEC_NATIVE, filename, argv, envp, + NULL, NULL, NULL); + return (-errno); + } + + 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); +} + +/* + * Linux currently defines 42 options for prctl (PR_CAPBSET_READ, + * PR_CAPBSET_DROP, etc.). Most of these are not emulated. + */ +long +lx_prctl(int option, uintptr_t arg2, uintptr_t arg3, + uintptr_t arg4, uintptr_t arg5) +{ + psinfo_t psinfo; + size_t fnamelen = sizeof (psinfo.pr_fname); + size_t psargslen = sizeof (psinfo.pr_psargs); + int fd; + + if (option == LX_PR_GET_DUMPABLE) { + /* Indicate that process is always dumpable */ + return (1); + } + + if (option == LX_PR_SET_DUMPABLE) { + if (arg2 != 1 && arg2 != 0) + return (-EINVAL); + /* Lie about altering process dumpability */ + return (0); + } + + if (option == LX_PR_SET_KEEPCAPS) { + /* + * The closest illumos analog to SET_KEEPCAPS is the PRIV_AWARE + * flag. There are probably some cases where it's not exactly + * the same, but this will do for a first try. + */ + if (arg2 == 0) { + if (setpflags(PRIV_AWARE_RESET, 1) != 0) + return (-errno); + } else { + if (setpflags(PRIV_AWARE, 1) != 0) + return (-errno); + } + return (0); + } + + if (option != LX_PR_SET_NAME) { + lx_unsupported("prctl option %d", option); + return (-ENOSYS); + } + + /* + * In Linux, PR_SET_NAME sets the name of the thread, not the process. + * Due to the historical quirks of Linux's asinine thread model, this + * name is effectively the name of the process (as visible via ps(1)) + * if the thread is the first of its task group. The first thread is + * therefore special, and to best mimic Linux semantics (and absent a + * notion of per-LWP names), we do nothing (but return success) on LWPs + * other than LWP 1. + */ + if (thr_self() != 1) + return (0); + + if (uucopy((void *)arg2, psinfo.pr_fname, + MIN(LX_PR_SET_NAME_NAMELEN, fnamelen)) != 0) + return (-errno); + + psinfo.pr_fname[fnamelen - 1] = '\0'; + + if (uucopy((void *)arg2, psinfo.pr_psargs, + MIN(LX_PR_SET_NAME_NAMELEN, psargslen)) != 0) + return (-errno); + + psinfo.pr_psargs[psargslen - 1] = '\0'; + + if ((fd = open("/native/proc/self/psinfo", O_WRONLY)) < 0) + return (-errno); + + if (pwrite(fd, psinfo.pr_fname, fnamelen, + (uintptr_t)psinfo.pr_fname - (uintptr_t)&psinfo) != fnamelen) { + (void) close(fd); + return (-EIO); + } + + if (pwrite(fd, psinfo.pr_psargs, psargslen, + (uintptr_t)psinfo.pr_psargs - (uintptr_t)&psinfo) != psargslen) { + (void) close(fd); + return (-EIO); + } + + (void) close(fd); + + return (0); +} + +/* + * For syslog(), as there is no kernel and nothing to log, we simply emulate a + * kernel cyclic buffer (LOG_BUF_LEN) of 0 bytes, only handling errors for bad + * input. All actions except 3 and 10 require CAP_SYS_ADMIN or CAP_SYSLOG, in + * lieu of full capabilities support for now we just perform an euid check. + */ +long +lx_syslog(int type, char *bufp, int len) +{ + if (type < 0 || type > 10) + return (-EINVAL); + + if ((type != 3 && type != 10) && (geteuid() != 0)) + return (-EPERM); + + if ((type >= 2 && type <= 4) && (bufp == NULL || len < 0)) + return (-EINVAL); + + if ((type == 8) && (len < 1 || len > 8)) + return (-EINVAL); + + return (0); +} + +/* + * The following are pass-through functions but we need to return the correct + * long so that the errno propagates back to the Linux code correctly. + */ + +long +lx_alarm(unsigned int seconds) +{ + int r; + + r = alarm(seconds); + return ((r == -1) ? -errno : r); +} + +long +lx_close(int fildes) +{ + int r; + + r = close(fildes); + return ((r == -1) ? -errno : r); +} + +long +lx_chdir(const char *path) +{ + int r; + + r = chdir(path); + return ((r == -1) ? -errno : r); +} + +long +lx_chroot(const char *path) +{ + int r; + + r = chroot(path); + return ((r == -1) ? -errno : r); +} + +long +lx_creat(const char *path, mode_t mode) +{ + int r; + + r = creat(path, mode); + return ((r == -1) ? -errno : r); +} + +long +lx_dup(int fildes) +{ + int r; + + r = dup(fildes); + return ((r == -1) ? -errno : r); +} + +long +lx_epoll_pwait(int epfd, void *events, int maxevents, int timeout, + const sigset_t *sigmask) +{ + int r; + + r = epoll_pwait(epfd, events, maxevents, timeout, sigmask); + return ((r == -1) ? -errno : r); +} + +long +lx_epoll_create(int size) +{ + int r; + + r = epoll_create(size); + return ((r == -1) ? -errno : r); +} + +long +lx_epoll_create1(int flags) +{ + int r; + + r = epoll_create1(flags); + return ((r == -1) ? -errno : r); +} + +long +lx_epoll_wait(int epfd, void *events, int maxevents, int timeout) +{ + int r; + + r = epoll_wait(epfd, events, maxevents, timeout); + return ((r == -1) ? -errno : r); +} + +long +lx_fchdir(int fildes) +{ + int r; + + r = fchdir(fildes); + return ((r == -1) ? -errno : r); +} + +long +lx_getgid(void) +{ + int r; + + r = getgid(); + return (r); +} + +long +lx_getgroups(int gidsetsize, gid_t *grouplist) +{ + int r; + + r = getgroups(gidsetsize, grouplist); + return ((r == -1) ? -errno : r); +} + +long +lx_getitimer(int which, struct itimerval *value) +{ + int r; + + r = getitimer(which, value); + return ((r == -1) ? -errno : r); +} + +long +lx_getuid(void) +{ + int r; + + r = getuid(); + return (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_mincore(caddr_t addr, size_t len, char *vec) +{ + int r; + + r = mincore(addr, len, vec); + if (r == -1 && errno == EINVAL) { + /* + * LTP mincore01 expects mincore with a huge len to fail with + * ENOMEM on a modern kernel but on Linux 2.6.11 and earlier it + * returns EINVAL. + */ + if (strcmp(lx_release, "2.6.11") > 0 && (long)len < 0) + errno = ENOMEM; + } + return ((r == -1) ? -errno : r); +} + +long +lx_munmap(void *addr, size_t len) +{ + int r; + + r = munmap(addr, len); + return ((r == -1) ? -errno : r); +} + +long +lx_nice(int incr) +{ + int r; + + r = nice(incr); + return ((r == -1) ? -errno : r); +} + +long +lx_nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + int r; + + r = nanosleep(rqtp, rmtp); + return ((r == -1) ? -errno : r); +} + +long +lx_pause(void) +{ + int r; + + r = pause(); + return ((r == -1) ? -errno : r); +} + +long +lx_setgid(gid_t gid) +{ + int r; + + r = setgid(gid); + return ((r == -1) ? -errno : r); +} + +long +lx_setuid(uid_t uid) +{ + int r; + + r = setuid(uid); + return ((r == -1) ? -errno : r); +} + +long +lx_setregid(gid_t rgid, gid_t egid) +{ + int r; + + r = setregid(rgid, egid); + return ((r == -1) ? -errno : r); +} + +long +lx_setreuid(uid_t ruid, uid_t euid) +{ + int r; + + r = setreuid(ruid, euid); + return ((r == -1) ? -errno : r); +} + +long +lx_shmdt(char *shmaddr) +{ + int r; + + r = shmdt(shmaddr); + return ((r == -1) ? -errno : r); +} + +long +lx_stime(const time_t *tp) +{ + int r; + + r = stime(tp); + return ((r == -1) ? -errno : r); +} + +long +lx_symlink(const char *name1, const char *name2) +{ + int r; + + r = symlink(name1, name2); + return ((r == -1) ? -errno : r); +} + +long +lx_umask(mode_t cmask) +{ + int r; + + r = umask(cmask); + 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); +} + +long +lx_vhangup(void) +{ + if (geteuid() != 0) + return (-EPERM); + + vhangup(); + + return (0); +} + +long +lx_eventfd(unsigned int initval) +{ + return (lx_eventfd2(initval, 0)); +} + +long +lx_eventfd2(unsigned int initval, int flags) +{ + int r = eventfd(initval, flags); + + /* + * eventfd(3C) may fail with ENOENT if /dev/eventfd is not available. + * It is less jarring to Linux programs to tell them that the system + * call is not supported than to report an error number they are not + * expecting. + */ + if (r == -1 && errno == ENOENT) + return (-ENOTSUP); + + return (r == -1 ? -errno : r); +} + +long +lx_timerfd_create(int clockid, int flags) +{ + int r = timerfd_create(clockid, flags); + + /* + * As with the eventfd case, above, 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); +} + +/* + * We lucked out here. Linux and Solaris have exactly the same + * rusage structures. + */ +long +lx_getrusage(uintptr_t p1, uintptr_t p2) +{ + int who = (int)p1; + struct rusage *rup = (struct rusage *)p2; + int rv, swho; + + if (who == LX_RUSAGE_SELF) + swho = RUSAGE_SELF; + else if (who == LX_RUSAGE_CHILDREN) + swho = RUSAGE_CHILDREN; + else if (who == LX_RUSAGE_THREAD) + swho = RUSAGE_LWP; + else + return (-EINVAL); + + rv = getrusage(swho, rup); + + return (rv < 0 ? -errno : 0); +} 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..746edd9370 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mount.c @@ -0,0 +1,901 @@ +/* + * 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 <alloca.h> +#include <assert.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <strings.h> +#include <nfs/mount.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/lx_autofs.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]; +}; + +typedef enum mount_opt_type { + MOUNT_OPT_INVALID = 0, + MOUNT_OPT_NORMAL = 1, /* option value: none */ + MOUNT_OPT_UINT = 2, /* option value: unsigned int */ + MOUNT_OPT_BYTESIZE = 3 /* option value: byte size, e.g. 25m */ +} mount_opt_type_t; + +typedef struct mount_opt { + char *mo_name; + mount_opt_type_t mo_type; +} mount_opt_t; + + +/* + * Globals + */ +mount_opt_t lofs_options[] = { + { NULL, MOUNT_OPT_INVALID } +}; + +mount_opt_t lx_proc_options[] = { + { NULL, MOUNT_OPT_INVALID } +}; + +mount_opt_t lx_sysfs_options[] = { + { NULL, MOUNT_OPT_INVALID } +}; + +mount_opt_t lx_tmpfs_options[] = { + { LX_MNTOPT_SIZE, MOUNT_OPT_BYTESIZE }, + { LX_MNTOPT_MODE, MOUNT_OPT_UINT }, + { NULL, MOUNT_OPT_INVALID } +}; + +mount_opt_t lx_autofs_options[] = { + { LX_MNTOPT_FD, MOUNT_OPT_UINT }, + { LX_MNTOPT_PGRP, MOUNT_OPT_UINT }, + { LX_MNTOPT_MINPROTO, MOUNT_OPT_UINT }, + { LX_MNTOPT_MAXPROTO, MOUNT_OPT_UINT }, + { NULL, MOUNT_OPT_INVALID } +}; + + +/* + * i_lx_opt_verify() - Check the mount options. + * + * You might wonder why we're being so strict about the mount options + * we allow. The reason is that normally all mount option verification + * is done by the Solaris userland mount command. Once mount options + * are passed to the kernel, invalid options are simply ignored. So + * if we actually want to catch requests for functionality that we + * don't support, or if we want to make sure that we don't randomly + * enable options that we haven't check to make sure they have the + * same syntax on Linux and Solaris, we need to reject any options + * we don't know to be ok here. + */ +static int +i_lx_opt_verify(char *opts, mount_opt_t *mop) +{ + int opts_len = strlen(opts); + char *opts_tmp, *opt; + int opt_len, i; + + assert((opts != NULL) && (mop != NULL)); + + /* If no options were specified, there's no problem. */ + if (opts_len == 0) { + errno = 0; + return (0); + } + + /* If no options are allowed, fail. */ + if (mop[0].mo_name == NULL) { + errno = ENOTSUP; + return (-1); + } + + /* Don't accept leading or trailing ','. */ + if ((opts[0] == ',') || (opts[opts_len] == ',')) { + errno = EINVAL; + return (-1); + } + + /* Don't accept sequential ','. */ + for (i = 1; i < opts_len; i++) { + if ((opts[i - 1] == ',') && (opts[i] == ',')) { + errno = EINVAL; + return (-1); + } + } + + /* + * We're going to use strtok() which modifies the target + * string so make a temporary copy. + */ + opts_tmp = SAFE_ALLOCA(opts_len); + if (opts_tmp == NULL) { + errno = ENOMEM; + return (-1); + } + bcopy(opts, opts_tmp, opts_len + 1); + + /* Verify each prop one at a time. */ + opt = strtok(opts_tmp, ","); + opt_len = strlen(opt); + for (;;) { + + /* Check for matching option/value pair. */ + for (i = 0; mop[i].mo_name != NULL; i++) { + char *ovalue; + int ovalue_len, mo_len; + + /* If the options is too short don't bother comparing */ + mo_len = strlen(mop[i].mo_name); + if (opt_len < mo_len) { + /* Keep trying to find a match. */ + continue; + } + + /* Compare the option to an allowed option. */ + if (strncmp(mop[i].mo_name, opt, mo_len) != 0) { + /* Keep trying to find a match. */ + continue; + } + + if (mop[i].mo_type == MOUNT_OPT_NORMAL) { + /* The option doesn't take a value. */ + if (opt_len == mo_len) { + /* This option is ok. */ + break; + } else { + /* Keep trying to find a match. */ + continue; + } + } + + /* This options takes a value. */ + if ((opt_len == mo_len) || (opt[mo_len] != '=')) { + /* Keep trying to find a match. */ + continue; + } + + /* We have an option match. Verify option value. */ + ovalue = &opt[mo_len] + 1; + ovalue_len = strlen(ovalue); + + /* Value can't be zero length string. */ + if (ovalue_len == 0) { + errno = EINVAL; + return (-1); + } + + if (mop[i].mo_type == MOUNT_OPT_UINT) { + int j; + /* Verify that value is an unsigned int. */ + for (j = 0; j < ovalue_len; j++) { + if (!isdigit(ovalue[j])) { + errno = EINVAL; + return (-1); + } + } + } else if (mop[i].mo_type == MOUNT_OPT_BYTESIZE) { + int j; + int stage = 0; + + /* + * Verify that the value is an unsigned integer + * that ends in a magnitude suffix (i.e. 'k' + * or 'm') or a '%' character. + */ + for (j = 0; j < ovalue_len; j++) { + switch (stage) { + case 0: + /* + * Look for at least one digit. + */ + if (!isdigit(ovalue[j])) { + errno = EINVAL; + return (-1); + } + stage = 1; + break; + case 1: + /* + * Allow an unlimited number of + * digits. + */ + if (isdigit(ovalue[j])) { + break; + } + /* + * Allow one (valid) byte + * magnitude character. + */ + if (ovalue[j] == 'm' || + ovalue[j] == 'k' || + ovalue[j] == '\%') { + stage = 2; + break; + } + errno = EINVAL; + return (-1); + case 2: + /* + * Invalid trailing characters. + */ + errno = EINVAL; + return (-1); + } + } + + if (stage < 1) { + errno = EINVAL; + return (-1); + } + } else { + /* Unknown option type specified. */ + assert(0); + } + + /* The option is ok. */ + break; + } + + /* If there were no matches this is an unsupported option. */ + if (mop[i].mo_name == NULL) { + errno = EINVAL; + return (-1); + } + + /* This option is ok, move onto the next option. */ + if ((opt = strtok(NULL, ",")) == NULL) + break; + opt_len = strlen(opt); + }; + + /* We verified all the options. */ + return (0); +} + +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); +} + +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; + + /* Variables needed for all mounts. */ + char source[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1]; + char target[MAXPATHLEN]; + char fstype[MAXPATHLEN], options[MAX_MNTOPT_STR]; + int sflags, rv; + + /* Variables needed for nfs mounts. */ + 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; + + /* Initialize Solaris 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); + + /* Make sure we support the requested mount flags. */ + if ((flags & ~LX_MS_SUPPORTED) != 0) { + lx_unsupported("unsupported mount flags: 0x%x", flags); + return (-ENOTSUP); + } + + /* Do filesystem specific mount work. */ + if (flags & LX_MS_BIND) { + + /* If MS_BIND is set, we turn this into a lofs mount. */ + (void) strcpy(fstype, "lofs"); + + /* Copy in Linux mount options. */ + if (datap != NULL) { + rv = uucopystr((void *)datap, + options, sizeof (options)); + if ((rv == -1) || (rv == sizeof (options))) + return (-EFAULT); + } + lx_debug("\tlinux mount options: \"%s\"", options); + + /* Verify Linux mount options. */ + if (i_lx_opt_verify(options, lofs_options) != 0) { + lx_unsupported("unsupported lofs mount options: %s", + options); + return (-errno); + } + } else if (strcmp(fstype, "tmpfs") == 0) { + + /* Copy in Linux mount options. */ + if (datap != NULL) { + rv = uucopystr((void *)datap, + options, sizeof (options)); + if ((rv == -1) || (rv == sizeof (options))) + return (-EFAULT); + } + lx_debug("\tlinux mount options: \"%s\"", options); + + /* Verify Linux mount options. */ + if (i_lx_opt_verify(options, lx_tmpfs_options) != 0) { + lx_unsupported("unsupported tmpfs mount options: %s", + options); + return (-errno); + } + } else if (strcmp(fstype, "proc") == 0) { + struct stat64 sb; + + /* Translate proc mount requests to lx_proc requests. */ + (void) strcpy(fstype, "lx_proc"); + + /* Copy in Linux mount options. */ + if (datap != NULL) { + rv = uucopystr((void *)datap, + options, sizeof (options)); + if ((rv == -1) || (rv == sizeof (options))) + return (-EFAULT); + } + lx_debug("\tlinux mount options: \"%s\"", options); + + /* Verify Linux mount options. */ + if (i_lx_opt_verify(options, lx_proc_options) != 0) { + lx_unsupported("unsupported proc mount options: %s", + options); + return (-errno); + } + + /* If mounting proc over itself, just return ok */ + if (stat64(target, &sb) == 0 && + strcmp(sb.st_fstype, "lx_proc") == 0) { + return (0); + } + } else if (strcmp(fstype, "sysfs") == 0) { + /* Translate sysfs mount requests to lx_sysfs requests. */ + (void) strcpy(fstype, "lx_sysfs"); + + /* Copy in Linux mount options. */ + if (datap != NULL) { + rv = uucopystr((void *)datap, + options, sizeof (options)); + if ((rv == -1) || (rv == sizeof (options))) + return (-EFAULT); + } + lx_debug("\tlinux mount options: \"%s\"", options); + + /* Verify Linux mount options. */ + if (i_lx_opt_verify(options, lx_sysfs_options) != 0) { + lx_unsupported("unsupported sysfs mount options: %s", + options); + return (-errno); + } + } else if (strcmp(fstype, "autofs") == 0) { + + /* Translate autofs mount requests to lx_afs requests. */ + (void) strcpy(fstype, LX_AUTOFS_NAME); + + /* Copy in Linux mount options. */ + if (datap != NULL) { + rv = uucopystr((void *)datap, + options, sizeof (options)); + if ((rv == -1) || (rv == sizeof (options))) + return (-EFAULT); + } + lx_debug("\tlinux mount options: \"%s\"", options); + + /* Verify Linux mount options. */ + if (i_lx_opt_verify(options, lx_autofs_options) != 0) { + lx_unsupported("unsupported autofs mount options: %s", + options); + return (-errno); + } + } else if (strcmp(fstype, "nfs") == 0) { + + /* + * 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 + * nfs_pre_mount(). The user-level mount caller is in charge of + * determining the format in which it passes the data parameter. + */ + int vers; + + 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) it is just a string. + */ + switch (vers) { + case 1: + case 2: + case 3: + case 5: + case 6: + lx_unsupported("unsupported nfs mount request " + "version: %d\n", vers); + return (-ENOTSUP); + + case 4: + 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); + + break; + + default: + /* + * Handle new 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)); + break; + } + + /* + * For nfs mounts we need to tell the mount system call + * to expect extra parameters. + */ + sflags |= MS_DATA; + sdataptr = (char *)&nfs_args; + sdatalen = sizeof (nfs_args); + } else { + lx_unsupported("unsupported mount filesystem type: %s", fstype); + return (-ENODEV); + } + + /* 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); + + return (mount(source, target, sflags, fstype, sdataptr, sdatalen, + options, sizeof (options)) ? -errno : 0); +} + +/* + * umount() is identical, though it is implemented on top of umount2() in + * Solaris so it cannot be a pass-thru system call. + */ +long +lx_umount(uintptr_t p1) +{ + return (umount((char *)p1) ? -errno : 0); +} + +/* + * The Linux umount2() system call is identical but has a different value for + * MNT_FORCE (the logical equivalent to MS_FORCE). + */ +#define LX_MNT_FORCE 0x1 + +long +lx_umount2(uintptr_t p1, uintptr_t p2) +{ + char *path = (char *)p1; + int flags = 0; + + if (p2 & ~LX_MNT_FORCE) + return (-EINVAL); + + if (p2 & LX_MNT_FORCE) + flags |= MS_FORCE; + + return (umount2(path, flags) ? -errno : 0); +} 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..983b4c66d9 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c @@ -0,0 +1,2155 @@ +/* + * 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 + * + * All of the Linux NFS mount RPC support is handled within the kernel whereas + * on Illumos the NFS mount command performs the initial RPC calls to contact + * the server's mountd, get the file handle, and negotiate security before + * making the actual 'mount' syscall. Thus we emulate the Linux in-kernel + * RPC behavior here using code that is partially based on the code from our + * user-level NFS mount command. This code also includes the nullproc RPC + * function calls + * + * In addition to the code described above we also have brand-specific code to + * convert the Linux mount arguments into our native format. + * + * Because libnsl (which we need to make RPCs) depends on the netconfig table + * (which won't exist inside an lx zone) we provide a built-in default + * netconfig table which we hook into libnsl via the brand callbacks. + * + * 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 2015 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 <sys/lx_mount.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_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_PASTHRU}, + {"cto", NULL, MOUNT_OPT_IGNORE}, + {"fg", NULL, MOUNT_OPT_PASTHRU}, + {"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_INVALID}, + {"mountproto", NULL, MOUNT_OPT_INVALID}, + {"mountvers", NULL, MOUNT_OPT_INVALID}, + {"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_PASTHRU}, + {"rsize", NULL, MOUNT_OPT_PASTHRU}, + {"sec", NULL, MOUNT_OPT_PASTHRU}, + {"sharecache", NULL, MOUNT_OPT_IGNORE}, + {"sloppy", NULL, MOUNT_OPT_INVALID}, + {"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. + * + * nmd_nfsvers_to_use is the actual version number found to use. It + * is determined in get_fh by pinging the various versions of the + * NFS service on the server to see which responds positively. + * + * nmd_nfsretry_vers is the version number set when we retry the mount + * command with the version decremented from nmd_nfsvers_to_use. + * nmd_nfsretry_vers is set from nmd_nfsvers_to_use when we retry the mount + * for errors other than RPC errors; it helps us know why we are + * retrying. It is an indication that the retry is due to non-RPC errors. + */ +typedef struct nfs_mnt_data { + int nmd_bg; + int nmd_posix; + int nmd_retries; + ushort_t nmd_nfs_port; + char *nmd_nfs_proto; + char *nmd_fstype; + seconfig_t nmd_nfs_sec; + int nmd_sec_opt; /* any security option ? */ + rpcvers_t nmd_nfsvers; + rpcvers_t nmd_nfsvers_to_use; + rpcvers_t nmd_nfsretry_vers; +} 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 + +#define NFS_ARGS_EXTB_secdata(args, secdata) \ + { (args)->nfs_args_ext = NFS_ARGS_EXTB, \ + (args)->nfs_ext_u.nfs_extB.secdata = secdata; } + +extern int __clnt_bindresvport(CLIENT *); + +static int retry(struct mnttab *, int, nfs_mnt_data_t *); +static int set_args(int *, struct nfs_args *, char *, struct mnttab *, + nfs_mnt_data_t *); +static int get_fh(struct nfs_args *, char *, char *, int *, + struct netconfig **, ushort_t, nfs_mnt_data_t *); +static int make_secure(struct nfs_args *, char *, struct netconfig *, + rpcvers_t, nfs_mnt_data_t *); +static int mount_nfs(struct mnttab *, int, err_ret_t *, nfs_mnt_data_t *); +static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **, + char *, ushort_t, err_ret_t *, bool_t, nfs_mnt_data_t *); +static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t, + struct netconfig **, char *, ushort_t, struct t_info *, + caddr_t *, char *, err_ret_t *); +static struct netbuf *get_the_addr(char *, rpcprog_t, rpcvers_t, + struct netconfig *, ushort_t, struct t_info *, caddr_t *, + char *, err_ret_t *); + +static int lx_nsl_set_sz_func(void); +static struct netconfig *lx_get_ent_func(int); + +/* + * These are the defaults (range) for the client when determining + * which NFS version to use when probing the server (see above). + * These will only be used when the vers mount option is not used. + */ +#define vers_max_default NFS_VERSMAX_DEFAULT +#define vers_min_default NFS_VERSMIN_DEFAULT + +/* + * The wnl/WNL* definitions come from cmd/fs.d/nfs/mount/webnfs.h. We + * incorporate those here since the cmd src tree hierarchy is not built when + * we're compiling the lib portion of the src tree and since these definitions + * are a fundamental part of the protocol spec, there is no risk of these + * changing (i.e. we're just like the Linux kernel here, which has these + * built-in). We only need the bare minimum set of definitions to make RPCs to + * the NFS server to negotiate the mount. + */ + +/* The timeout for our mount null proc pings is always 5 seconds. */ +static struct timeval TIMEOUT = { 5, 0 }; +#define WNLPROC_NULL 0 +#define WNLPROC3_NULL 0 +#define WNLPROC4_NULL 0 +#define WNL_FHSIZE 32 + +static enum clnt_stat +wnlproc_null_2(void *argp, void *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, WNLPROC_NULL, (xdrproc_t)xdr_void, + (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT)); +} + +static enum clnt_stat +wnlproc3_null_3(void *argp, void *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, WNLPROC3_NULL, (xdrproc_t)xdr_void, + (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT)); +} + +static enum clnt_stat +wnlproc4_null_4(void *argp, void *clnt_res, CLIENT *clnt) +{ + return (clnt_call(clnt, WNLPROC4_NULL, (xdrproc_t)xdr_void, + (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT)); +} + +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); +} + +/* + * We can return a negative error value which is the errno we need to return to + * lx or we can return a positive error value which is primarily used to + * indicate retry (otherwise we map to -EINVAL in the caller). Returning 0 + * indicates success. + */ +static int +mount_nfs(struct mnttab *mntp, int mntflags, err_ret_t *retry_error, + nfs_mnt_data_t *nmdp) +{ + struct nfs_args *argp = NULL; + struct netconfig *nconf = NULL; + int vers = 0; + int r = 0; + ushort_t port; + char *colonp; + char *path; + char *host; + char spec_buf[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1]; + + mntp->mnt_fstype = MNTTYPE_NFS; + + (void) strlcpy(spec_buf, mntp->mnt_special, 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); + + memset(argp, 0, sizeof (*argp)); + memset(&nmdp->nmd_nfs_sec, 0, sizeof (seconfig_t)); + nmdp->nmd_sec_opt = 0; + port = 0; + + if ((r = set_args(&mntflags, argp, host, mntp, nmdp)) != 0) + goto out; + + if (port == 0) { + port = nmdp->nmd_nfs_port; + } else if (nmdp->nmd_nfs_port != 0 && nmdp->nmd_nfs_port != port) { + r = -EINVAL; + goto out; + } + + r = get_fh(argp, host, path, &vers, &nconf, port, nmdp); + if (r != 0) { + /* All attempts failed */ + if (r == RET_MNTERR) { + r = -EREMOTE; + } else if (r != RET_RETRY) { + r = -EAGAIN; + } + goto out; + } + + /* + * Call to get_fh() above may have obtained the netconfig info and NULL + * proc'd the server. This would be the case with v4 + */ + if (!(argp->flags & NFSMNT_KNCONF)) { + nconf = NULL; + r = getaddr_nfs(argp, host, &nconf, path, port, + retry_error, TRUE, nmdp); + if (r != 0) { + if (r != RET_RETRY) + r = -EAGAIN; + goto out; + } + } + + if (make_secure(argp, host, nconf, vers, nmdp) < 0) { + r = -EAGAIN; + goto out; + } + + mntflags |= MS_DATA | MS_OPTIONSTR; + + r = mount(mntp->mnt_special, mntp->mnt_mountp, mntflags, + nmdp->nmd_fstype, argp, sizeof (*argp), mntp->mnt_mntopts, + MAX_MNTOPT_STR); + if (r != 0) + r = -r; +out: + 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); + } + nfs_free_secdata(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); + + return (r); +} + +/* + * These options are duplicated in uts/common/fs/nfs/nfs_dlinet.c + * Changes must be made to both lists. + */ +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, + 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, struct mnttab *mnt, + nfs_mnt_data_t *nmdp) +{ + char *saveopt, *optstr, *opts, *newopts, *val; + int num; + int largefiles = 0; + int invalid = 0; + int attrpref = 0; + int optlen; + + args->flags = NFSMNT_INT; /* default is "intr" */ + args->flags |= NFSMNT_HOSTNAME; + args->flags |= NFSMNT_NEWARGS; /* using extented nfs_args structure */ + args->hostname = fshost; + + optstr = opts = strdup(mnt->mnt_mntopts); + /* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */ + optlen = strlen(mnt->mnt_mntopts) + 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 = htons((ushort_t)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: + nmdp->nmd_bg++; + break; + case OPT_FG: + nmdp->nmd_bg = 0; + break; + case OPT_RETRY: + if (convert_int(&nmdp->nmd_retries, val) != 0) + goto badopt; + 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/nffssec.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 if (strcmp(val, "dh") == 0) { + (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, + "dh", MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = + nmdp->nmd_nfs_sec.sc_rpcnum = 3; + } 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; + default: + invalid = 1; + break; + } + if (!invalid) { + if (newopts[0]) + strcat(newopts, ","); + strcat(newopts, saveopt); + } + } + /* Default is to turn extended attrs on */ + if (!attrpref) { + if (newopts[0]) + strcat(newopts, ","); + strcat(newopts, MNTOPT_XATTR); + } + strcpy(mnt->mnt_mntopts, newopts); + 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 && nmdp->nmd_nfs_proto != NULL && + strncasecmp(nmdp->nmd_nfs_proto, NC_UDP, strlen(NC_UDP)) == 0) + return (-EINVAL); + + return (0); + +badopt: + free(optstr); + return (-EINVAL); +} + +/* + * NFS project private API. + * + * Free an sec_data structure. + * Free the parts that nfs_clnt_secdata allocates. + */ +void +nfs_free_secdata(sec_data_t *secdata) +{ + dh_k4_clntdata_t *dkdata; + + if (!secdata) + return; + + switch (secdata->rpcflavor) { + case AUTH_UNIX: + case AUTH_NONE: + break; + + case AUTH_DES: + /* LINTED pointer alignment */ + dkdata = (dh_k4_clntdata_t *)secdata->data; + if (dkdata) { + if (dkdata->netname) + free(dkdata->netname); + if (dkdata->syncaddr.buf) + free(dkdata->syncaddr.buf); + free(dkdata); + } + break; + + default: + break; + } + + free(secdata); +} + +/* + * Make an client side sec_data structure and fill in appropriate value + * based on its rpc security flavor. + * + * It is caller's responsibility to allocate space for seconfig_t, + * and this routine will allocate space for the sec_data structure + * and related data field. + * + * Return the sec_data_t on success. + * If fail, return NULL pointer. + */ +sec_data_t * +nfs_clnt_secdata(seconfig_t *secp, char *hostname, struct knetconfig *knconf, + struct netbuf *syncaddr, int flags) +{ + char netname[MAXNETNAMELEN+1]; + sec_data_t *secdata; + dh_k4_clntdata_t *dkdata; + + secdata = malloc(sizeof (sec_data_t)); + if (!secdata) + return (NULL); + + (void) memset(secdata, 0, sizeof (sec_data_t)); + + secdata->secmod = secp->sc_nfsnum; + secdata->rpcflavor = secp->sc_rpcnum; + secdata->uid = secp->sc_uid; + secdata->flags = flags; + + /* + * Now, fill in the information for client side secdata : + * + * For AUTH_UNIX, AUTH_DES + * hostname can be in the form of + * nodename or + * nodename.domain + */ + switch (secp->sc_rpcnum) { + case AUTH_UNIX: + case AUTH_NONE: + secdata->data = NULL; + break; + + case AUTH_DES: + if (!host2netname(netname, hostname, NULL)) + goto err_out; + + dkdata = malloc(sizeof (dh_k4_clntdata_t)); + if (!dkdata) + goto err_out; + + (void) memset((char *)dkdata, 0, + sizeof (dh_k4_clntdata_t)); + if ((dkdata->netname = strdup(netname)) == NULL) + goto err_out; + + dkdata->netnamelen = strlen(netname); + dkdata->knconf = knconf; + dkdata->syncaddr = *syncaddr; + dkdata->syncaddr.buf = malloc(syncaddr->len); + if (dkdata->syncaddr.buf == NULL) + goto err_out; + + (void) memcpy(dkdata->syncaddr.buf, syncaddr->buf, + syncaddr->len); + secdata->data = (caddr_t)dkdata; + break; + + default: + goto err_out; + } + + return (secdata); + +err_out: + free(secdata); + return (NULL); +} + +static int +make_secure(struct nfs_args *args, char *hostname, struct netconfig *nconf, + rpcvers_t vers, nfs_mnt_data_t *nmdp) +{ + sec_data_t *secdata; + int flags; + struct netbuf *syncaddr = NULL; + struct nd_addrlist *retaddrs = NULL; + char netname[MAXNETNAMELEN+1]; + + /* + * check to see if any secure mode is requested. + * if not, use default security mode. + */ + if (!nmdp->nmd_sec_opt) { + /* AUTH_UNIX is the default. */ + strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys", MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = 1; + args->flags |= NFSMNT_SECDEFAULT; + } + + /* + * Get the network address for the time service on the server. + * If an RPC based time service is not available then try the + * IP time service. + * + * This is for AUTH_DH processing. We will also pass down syncaddr + * and netname for NFS V4 even if AUTH_DH is not requested right now. + * NFS V4 does security negotiation in the kernel via SECINFO. + * This information might be needed later in the kernel. + */ + flags = 0; + syncaddr = NULL; + + if (nmdp->nmd_nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) { + /* + * If using nfsv4, we will not contact the remote RPCBINDer, + * since it is possibly behind a firewall. + */ + if (vers != NFS_V4) + syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS, + nconf, 0, NULL, NULL, NULL, NULL); + + if (syncaddr != NULL) { + /* for flags in sec_data */ + flags |= AUTH_F_RPCTIMESYNC; + } else { + /* + * TBD netdir_getbyname wants to lookup the timeserver + * entry in the /etc/services file (but our libnsl to do + * this won't work in Linux). That entry is: + * timed 525/udp timeserver + * Since we haven't implemented the emulation for + * netdir_getbyname yet, we'll simply return an error. + */ + struct nd_hostserv hs; + int error; + + hs.h_host = hostname; + hs.h_serv = "timserver"; + + /* XXX */ + if (1) + return (-1); + + error = netdir_getbyname(nconf, &hs, &retaddrs); + + if (error != ND_OK && + (nmdp->nmd_nfs_sec.sc_rpcnum == AUTH_DH)) + return (-1); + + if (error == ND_OK) + syncaddr = retaddrs->n_addrs; + + /* + * For NFS_V4 if AUTH_DH is negotiated later in the + * kernel thru SECINFO, it will need syncaddr + * and netname data. + */ + if (vers == NFS_V4 && syncaddr && + host2netname(netname, hostname, NULL)) { + args->syncaddr = malloc(sizeof (struct netbuf)); + args->syncaddr->buf = malloc(syncaddr->len); + (void) memcpy(args->syncaddr->buf, + syncaddr->buf, syncaddr->len); + args->syncaddr->len = syncaddr->len; + args->syncaddr->maxlen = syncaddr->maxlen; + args->netname = strdup(netname); + args->flags |= NFSMNT_SECURE; + } + } + } + + /* + * For the initial chosen flavor (any flavor defined in nfssec.conf), + * the data will be stored in the sec_data structure via + * nfs_clnt_secdata() and be passed to the kernel via nfs_args_* + * extended data structure. + */ + if (!(secdata = nfs_clnt_secdata(&nmdp->nmd_nfs_sec, hostname, + args->knconf, syncaddr, flags))) { + if (flags & AUTH_F_RPCTIMESYNC) { + free(syncaddr->buf); + free(syncaddr); + } else if (retaddrs) + netdir_free((void *)retaddrs, ND_ADDRLIST); + return (-1); + } + + NFS_ARGS_EXTB_secdata(args, secdata); + if (flags & AUTH_F_RPCTIMESYNC) { + free(syncaddr->buf); + free(syncaddr); + } else if (retaddrs) + netdir_free((void *)retaddrs, ND_ADDRLIST); + return (0); +} + +/* + * Get the network address on "hostname" for program "prog" + * with version "vers" by using the nconf configuration data + * passed in. + * + * If the address of a netconfig pointer is null then + * information is not sufficient and no netbuf will be returned. + * + * Finally, ping the null procedure of that service. + * + * A similar routine is also defined in ../../autofs/autod_nfs.c. + * This is a potential routine to move to ../lib for common usage. + */ +static struct netbuf * +get_the_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, + struct netconfig *nconf, ushort_t port, struct t_info *tinfo, + caddr_t *fhp, char *fspath, err_ret_t *error) +{ + struct netbuf *nb = NULL; + struct t_bind *tbind = NULL; + CLIENT *cl = NULL; + int fd = -1; + AUTH *ah = NULL; + AUTH *new_ah = NULL; + struct rpc_err r_err; + enum clnt_stat rc; + + if (nconf == NULL) + return (NULL); + + 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) + goto done; + + if (vers == NFS_V4) { + /* SET_ERR_RET(error, ERR_NOHOST, 1); */ + goto done; + } + + if (rpcb_getaddr(prog, vers, nconf, &tbind->addr, + hostname) == FALSE) { + goto done; + } + + if (port) { + /* LINTED pointer alignment */ + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + ((struct sockaddr_in *)tbind->addr.buf)->sin_port + = port; + else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port + = port; + + } + + cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0); + if (cl == NULL) { + /* + * clnt_tli_create() returns either RPC_SYSTEMERROR, + * RPC_UNKNOWNPROTO or RPC_TLIERROR. The RPC_TLIERROR translates + * to "Misc. TLI error". This is not too helpful. Most likely + * the connection to the remote server timed out, so this + * error is at least less perplexing. + * See: usr/src/cmd/rpcinfo/rpcinfo.c + */ + if (rpc_createerr.cf_stat == RPC_TLIERROR) { + SET_ERR_RET(error, ERR_RPCERROR, RPC_PMAPFAILURE); + } else { + SET_ERR_RET(error, ERR_RPCERROR, rpc_createerr.cf_stat); + } + goto done; + } + + ah = authsys_create_default(); + if (ah != NULL) + cl->cl_auth = ah; + + /* + * NULL procedures need not have an argument or + * result param. + */ + if (vers == NFS_VERSION) + rc = wnlproc_null_2(NULL, NULL, cl); + else if (vers == NFS_V3) + rc = wnlproc3_null_3(NULL, NULL, cl); + else + rc = wnlproc4_null_4(NULL, NULL, cl); + + if (rc != RPC_SUCCESS) { + clnt_geterr(cl, &r_err); + if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { + switch (r_err.re_status) { + case RPC_TLIERROR: + case RPC_CANTRECV: + case RPC_CANTSEND: + r_err.re_status = RPC_PROGVERSMISMATCH; + break; + default: + break; + } + } + SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status); + goto done; + } + + /* + * Make a copy of the netbuf to return + */ + nb = (struct netbuf *)malloc(sizeof (*nb)); + if (nb == NULL) + goto done; + + *nb = tbind->addr; + nb->buf = (char *)malloc(nb->maxlen); + if (nb->buf == NULL) { + free(nb); + nb = NULL; + goto done; + } + (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); + +done: + if (cl) { + if (ah != NULL) { + if (new_ah != NULL) + AUTH_DESTROY(ah); + AUTH_DESTROY(cl->cl_auth); + cl->cl_auth = NULL; + } + clnt_destroy(cl); + cl = NULL; + } + if (tbind) { + t_free((char *)tbind, T_BIND); + tbind = NULL; + } + if (fd >= 0) + (void) t_close(fd); + return (nb); +} + +static int +check_nconf(struct netconfig *nconf, int nthtry, int *valid_proto) +{ + int try_test = 0; + char *proto = NULL; + + if (nthtry == FIRST_TRY) { + try_test = ((nconf->nc_semantics == NC_TPI_COTS_ORD) || + (nconf->nc_semantics == NC_TPI_COTS)); + proto = NC_TCP; + } else if (nthtry == SECOND_TRY) { + try_test = (nconf->nc_semantics == NC_TPI_CLTS); + proto = NC_UDP; + } + + if (proto && + (strcmp(nconf->nc_protofmly, NC_INET) == 0 || + strcmp(nconf->nc_protofmly, NC_INET6) == 0) && + (strcmp(nconf->nc_proto, proto) == 0)) + *valid_proto = TRUE; + else + *valid_proto = FALSE; + + return (try_test); +} + +static struct netconfig * +netconfig_dup(struct netconfig *netconfigp) +{ + struct netconfig *nconf; + + nconf = calloc(1, sizeof (struct netconfig)); + if (nconf == NULL) + goto nomem; + + if ((nconf->nc_netid = strdup(netconfigp->nc_netid)) == NULL) + goto nomem; + + if ((nconf->nc_protofmly = strdup(netconfigp->nc_protofmly)) == NULL) + goto nomem; + + if ((nconf->nc_proto = strdup(netconfigp->nc_proto)) == NULL) + goto nomem; + + if ((nconf->nc_device = strdup(netconfigp->nc_device)) == NULL) + goto nomem; + + nconf->nc_lookups = NULL; + nconf->nc_nlookups = netconfigp->nc_nlookups; + nconf->nc_flag = netconfigp->nc_flag; + nconf->nc_semantics = netconfigp->nc_semantics; + return (nconf); + +nomem: + if (nconf != NULL) { + free(nconf->nc_netid); + free(nconf->nc_protofmly); + free(nconf->nc_proto); + free(nconf->nc_device); + free(nconf); + } + return (NULL); +} + +/* + * Get a network address on "hostname" for program "prog" + * with version "vers". If the port number is specified (non zero) + * then try for a TCP/UDP transport and set the port number of the + * resulting IP address. + * + * If the address of a netconfig pointer was passed and + * if it's not null, use it as the netconfig otherwise + * assign the address of the netconfig that was used to + * establish contact with the service. + * + * "error" refers to a more descriptive term when get_addr fails + * and returns NULL: ERR_PROTO_NONE if no error introduced by + * -o proto option, ERR_NETPATH if error found in NETPATH + * environment variable, ERR_PROTO_INVALID if an unrecognized + * protocol is specified by user, and ERR_PROTO_UNSUPP for a + * recognized but invalid protocol (eg. ticlts, ticots, etc.). + * "error" is ignored if get_addr returns non-NULL result. + * + */ +static struct netbuf * +get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, + struct netconfig **nconfp, char *proto, ushort_t port, + struct t_info *tinfo, caddr_t *fhp, char *fspath, err_ret_t *error) +{ + struct netbuf *nb = NULL; + struct netconfig *nconf = NULL; + int nci; + int nthtry = FIRST_TRY; + err_ret_t errsave_nohost, errsave_rpcerr; + + SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0); + SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0); + + SET_ERR_RET(error, ERR_PROTO_NONE, 0); + + if (nconfp && *nconfp) + return (get_the_addr(hostname, prog, vers, *nconfp, port, + tinfo, fhp, fspath, error)); + /* + * No nconf passed in. + * + * First search for COTS, second for CLTS unless proto + * is specified. When we retry, we reset the + * netconfig list so that we would search the whole list + * all over again. + */ + + /* + * If proto is specified, then only search for the match, + * otherwise try COTS first, if failed, try CLTS. + */ + if (proto) { + /* no matching proto name */ + SET_ERR_RET(error, ERR_PROTO_INVALID, 0); + + for (nci = 0; nci < N_NETCONF_ENTS; nci++) { + nconf = &nca[nci]; + if (strcmp(nconf->nc_netid, proto) != 0) + continue; + + /* may be unsupported */ + SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0); + + nb = get_the_addr(hostname, prog, vers, nconf, port, + tinfo, fhp, fspath, error); + if (nb != NULL) + break; + + /* nb is NULL - deal with errors */ + if (error) { + if (error->error_type == ERR_NOHOST) + SET_ERR_RET(&errsave_nohost, + error->error_type, + error->error_value); + if (error->error_type == ERR_RPCERROR) + SET_ERR_RET(&errsave_rpcerr, + error->error_type, + error->error_value); + } + + /* continue with same protocol selection */ + continue; + } /* end of while */ + + if (nci >= N_NETCONF_ENTS) + goto done; + + if (nb == NULL && + (nb = get_the_addr(hostname, prog, vers, nconf, port, + tinfo, fhp, fspath, error)) == NULL) + goto done; + } else { +retry: + SET_ERR_RET(error, ERR_NETPATH, 0); + for (nci = 0; nci < N_NETCONF_ENTS; nci++) { + int valid_proto; + + nconf = &nca[nci]; + SET_ERR_RET(error, ERR_PROTO_NONE, 0); + + if (check_nconf(nconf, nthtry, &valid_proto)) { + if (port != 0 && valid_proto != TRUE) + continue; + + nb = get_the_addr(hostname, prog, vers, nconf, + port, tinfo, fhp, fspath, error); + if (nb != NULL) + break; + + /* nb is NULL - deal with errors */ + if (error) { + if (error->error_type == ERR_NOHOST) + SET_ERR_RET(&errsave_nohost, + error->error_type, + error->error_value); + + if (error->error_type == ERR_RPCERROR) + SET_ERR_RET(&errsave_rpcerr, + error->error_type, + error->error_value); + } + + /* + * Continue the same search path in the + * netconfig db until no more matched + * nconf. + */ + } + } + + if (nci >= N_NETCONF_ENTS) { + if (++nthtry <= MNT_PREF_LISTLEN) + goto retry; + goto done; + } + + } + SET_ERR_RET(error, ERR_PROTO_NONE, 0); + + /* + * Got nconf and nb. Now dup the netconfig structure + * and return it thru nconfp. + */ + *nconfp = netconfig_dup(nconf); + if (*nconfp == NULL) { + free(nb); + nb = NULL; + } +done: + if (nb == NULL) { + /* + * Check the saved errors. The RPC error has * + * precedence over the no host error. + */ + if (errsave_nohost.error_type != ERR_PROTO_NONE) + SET_ERR_RET(error, errsave_nohost.error_type, + errsave_nohost.error_value); + + if (errsave_rpcerr.error_type != ERR_PROTO_NONE) + SET_ERR_RET(error, errsave_rpcerr.error_type, + errsave_rpcerr.error_value); + } + + return (nb); +} + +static int +lx_nsl_set_sz_func() +{ + return (N_NETCONF_ENTS); +} + +static struct netconfig * +lx_get_ent_func(int pos) +{ + struct netconfig *nconf; + + if (pos < 0 || pos >= N_NETCONF_ENTS) + return (NULL); + + nconf = &nca[pos]; + return (nconf); +} + +/* + * get fhandle of remote path from server's mountd + */ +static int +get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp, + struct netconfig **nconfp, ushort_t port, nfs_mnt_data_t *nmdp) +{ + struct fhstatus fhs; + struct mountres3 mountres3; + struct pathcnf p; + nfs_fh3 *fh3p; + struct timeval timeout = { 25, 0}; + CLIENT *cl; + enum clnt_stat rpc_stat; + rpcvers_t outvers = 0; + rpcvers_t vers_to_try; + rpcvers_t vers_min = vers_min_default; + int count, i, *auths; + + bzero(&fhs, sizeof (fhs)); + bzero(&mountres3, sizeof (mountres3)); + bzero(&p, sizeof (p)); + + switch (nmdp->nmd_nfsvers) { + case 2: /* version 2 specified try that only */ + vers_to_try = MOUNTVERS_POSIX; + vers_min = MOUNTVERS; + break; + case 3: /* version 3 specified try that only */ + vers_to_try = MOUNTVERS3; + vers_min = MOUNTVERS3; + break; + case 4: /* version 4 specified try that only */ + /* + * This assignment is in the wrong version sequence. + * The above are MOUNT program and this is NFS + * program. However, it happens to work out since the + * two don't collide for NFSv4. + */ + vers_to_try = NFS_V4; + vers_min = NFS_V4; + break; + default: /* no version specified, start with default */ + /* + * If the retry version is set, use that. This will + * be set if the last mount attempt returned any other + * besides an RPC error. + */ + if (nmdp->nmd_nfsretry_vers) + vers_to_try = nmdp->nmd_nfsretry_vers; + else { + vers_to_try = vers_max_default; + vers_min = vers_min_default; + } + + break; + } + + /* + * In the case of version 4, just NULL proc the server since + * there is no MOUNT program. If this fails, then decrease + * vers_to_try and continue on with regular MOUNT program + * processing. + */ + if (vers_to_try == NFS_V4) { + int savevers = nmdp->nmd_nfsvers_to_use; + err_ret_t error; + int retval; + SET_ERR_RET(&error, ERR_PROTO_NONE, 0); + + /* Let's hope for the best */ + nmdp->nmd_nfsvers_to_use = NFS_V4; + retval = getaddr_nfs(args, fshost, nconfp, + fspath, port, &error, vers_min == NFS_V4, nmdp); + + if (retval == RET_OK) { + *versp = nmdp->nmd_nfsvers_to_use = NFS_V4; + nmdp->nmd_fstype = MNTTYPE_NFS4; + args->fh = strdup(fspath); + if (args->fh == NULL) { + *versp = nmdp->nmd_nfsvers_to_use = savevers; + return (RET_ERR); + } + return (RET_OK); + } + nmdp->nmd_nfsvers_to_use = savevers; + + vers_to_try--; + /* If no more versions to try, let the user know. */ + if (vers_to_try < vers_min) + return (retval); + + /* + * If we are here, there are more versions to try but + * there has been an error of some sort. If it is not + * an RPC error (e.g. host unknown), we just stop and + * return the error since the other versions would see + * the same error as well. + */ + if (retval == RET_ERR && error.error_type != ERR_RPCERROR) + return (retval); + } + + while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers, + vers_min, vers_to_try, NULL)) == NULL) { + if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) + return (RET_ERR); + + /* + * We don't want to downgrade version on lost packets + */ + if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) || + (rpc_createerr.cf_stat == RPC_PMAPFAILURE)) + return (RET_RETRY); + + /* + * back off and try the previous version - patch to the + * problem of version numbers not being contigous and + * clnt_create_vers failing (SunOS4.1 clients & SGI servers) + * The problem happens with most non-Sun servers who + * don't support mountd protocol #2. So, in case the + * call fails, we re-try the call anyway. + */ + vers_to_try--; + if (vers_to_try < vers_min) { + if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH) + return (RET_ERR); + + return (RET_RETRY); + } + } + if (nmdp->nmd_posix && outvers < MOUNTVERS_POSIX) { + clnt_destroy(cl); + return (RET_ERR); + } + + if (__clnt_bindresvport(cl) < 0) { + clnt_destroy(cl); + return (RET_RETRY); + } + + if ((cl->cl_auth = authsys_create_default()) == NULL) { + clnt_destroy(cl); + return (RET_RETRY); + } + + switch (outvers) { + case MOUNTVERS: + case MOUNTVERS_POSIX: + *versp = nmdp->nmd_nfsvers_to_use = NFS_VERSION; + 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, "")); + clnt_destroy(cl); + return (RET_RETRY); + } + + if ((errno = fhs.fhs_status) != MNT_OK) { + clnt_destroy(cl); + return (RET_MNTERR); + } + args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle)); + if (args->fh == NULL) + return (RET_ERR); + + 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); + clnt_destroy(cl); + return (RET_RETRY); + } + if (_PC_ISSET(_PC_ERROR, p.pc_mask)) { + free(args->fh); + clnt_destroy(cl); + return (RET_ERR); + } + args->flags |= NFSMNT_POSIX; + args->pathconf = malloc(sizeof (p)); + if (args->pathconf == NULL) { + free(args->fh); + clnt_destroy(cl); + return (RET_ERR); + } + memcpy((caddr_t)args->pathconf, (caddr_t)&p, + sizeof (p)); + } + break; + + case MOUNTVERS3: + *versp = nmdp->nmd_nfsvers_to_use = NFS_V3; + 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, "")); + clnt_destroy(cl); + return (RET_RETRY); + } + + /* + * Assume here that most of the MNT3ERR_* + * codes map into E* errors. + */ + if ((errno = mountres3.fhs_status) != MNT_OK) { + clnt_destroy(cl); + return (RET_MNTERR); + } + + fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p)); + if (fh3p == NULL) + return (RET_ERR); + + 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; + + /* + * Check the security flavor to be used. + * + * If "secure" or "sec=flavor" is a mount + * option, check if the server supports the "flavor". + * If the server does not support the flavor, return + * error. + * + * If no mount option is given then look for default auth + * (default auth entry in /etc/nfssec.conf) in the auth list + * returned from server. If default auth not found, then use + * the first supported security flavor (by the client) in the + * auth list returned from the server. + * + */ + auths = + mountres3.mountres3_u.mountinfo.auth_flavors + .auth_flavors_val; + count = + mountres3.mountres3_u.mountinfo.auth_flavors + .auth_flavors_len; + + if (count <= 0) { + clnt_destroy(cl); + return (RET_ERR); + } + + if (nmdp->nmd_sec_opt) { + for (i = 0; i < count; i++) { + if (auths[i] == nmdp->nmd_nfs_sec.sc_nfsnum) + break; + } + if (i == count) + goto autherr; + } else { + /* AUTH_UNIX is the default. */ + strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys", MAX_NAME_LEN); + nmdp->nmd_nfs_sec.sc_nfsnum = 1; + } + break; + default: + clnt_destroy(cl); + return (RET_ERR); + } + + clnt_destroy(cl); + return (RET_OK); + +autherr: + clnt_destroy(cl); + return (RET_ERR); +} + +/* + * 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, + char *fspath, ushort_t port, err_ret_t *error, bool_t print_rpcerror, + nfs_mnt_data_t *nmdp) +{ + struct stat sb; + struct netconfig *nconf; + struct knetconfig *knconfp; + struct t_info tinfo; + err_ret_t addr_error; + + SET_ERR_RET(error, ERR_PROTO_NONE, 0); + SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0); + + args->addr = get_addr(fshost, NFS_PROGRAM, nmdp->nmd_nfsvers_to_use, + nconfp, nmdp->nmd_nfs_proto, port, &tinfo, &args->fh, fspath, + &addr_error); + + if (args->addr == NULL) { + switch (addr_error.error_type) { + case 0: + break; + case ERR_RPCERROR: + if (!print_rpcerror) + /* no error print at this time */ + break; + log_err("%s NFS service not available %s\n", fshost, + clnt_sperrno(addr_error.error_value)); + break; + case ERR_NETPATH: + log_err("%s: Error in NETPATH.\n", fshost); + break; + case ERR_PROTO_INVALID: + log_err("%s: NFS service does not recognize " + "protocol: %s.\n", fshost, nmdp->nmd_nfs_proto); + break; + case ERR_PROTO_UNSUPP: + if (nmdp->nmd_nfsvers || + nmdp->nmd_nfsvers_to_use == NFS_VERSMIN) { + log_err("%s: NFS service does" + " not support protocol: %s.\n", + fshost, nmdp->nmd_nfs_proto); + } + break; + case ERR_NOHOST: + log_err("%s: %s\n", fshost, "Unknown host"); + break; + default: + /* case ERR_PROTO_NONE falls through */ + log_err("%s: NFS service not responding\n", fshost); + break; + } + + SET_ERR_RET(error, + addr_error.error_type, addr_error.error_value); + if (addr_error.error_type == ERR_PROTO_NONE) + return (RET_RETRY); + else if (addr_error.error_type == ERR_RPCERROR && + !IS_UNRECOVERABLE_RPC(addr_error.error_value)) { + return (RET_RETRY); + } else if (nmdp->nmd_nfsvers == 0 && + addr_error.error_type == ERR_PROTO_UNSUPP && + nmdp->nmd_nfsvers_to_use != NFS_VERSMIN) { + /* + * If no version is specified, and the error is due + * to an unsupported transport, then decrement the + * version and retry. + */ + return (RET_RETRY); + } else + return (RET_ERR); + } + nconf = *nconfp; + + if (stat(nconf->nc_device, &sb) < 0) + return (RET_ERR); + + knconfp = (struct knetconfig *)malloc(sizeof (*knconfp)); + if (!knconfp) + return (RET_ERR); + + knconfp->knc_semantics = nconf->nc_semantics; + knconfp->knc_protofmly = nconf->nc_protofmly; + knconfp->knc_proto = nconf->nc_proto; + knconfp->knc_rdev = sb.st_rdev; + + /* 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; + } + + args->flags |= NFSMNT_KNCONF; + args->knconf = knconfp; + return (RET_OK); +} + +static int +retry(struct mnttab *mntp, int mntflags, nfs_mnt_data_t *nmdp) +{ + int delay = 5; + int count = nmdp->nmd_retries; + int r = -EAGAIN; + char *p; + + if (nmdp->nmd_bg) { + if (fork() > 0) + return (0); + } else { + p = strchr(mntp->mnt_special, ':'); + if (p != NULL) + *p = '\0'; + log_err("%s: server not responding\n", mntp->mnt_special); + if (p != NULL) + *p = ':'; + } + + while (count--) { + err_ret_t retry_error; + + if ((r = mount_nfs(mntp, mntflags, &retry_error, nmdp)) == 0) + return (0); + + if (r != RET_RETRY) + break; + + if (count > 0) { + (void) sleep(delay); + delay *= 2; + if (delay > 120) + delay = 120; + } + p = strchr(mntp->mnt_special, ':'); + if (p != NULL) + *p = '\0'; + log_err("%s: server not responding\n", mntp->mnt_special); + if (p != NULL) + *p = ':'; + } + + if (!nmdp->nmd_nfsretry_vers) + log_err("giving up on: %s\n", mntp->mnt_mountp); + + if (r > 0) + r = -EAGAIN; + return (r); +} + +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: + 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 + */ +static int +convert_nfs_arg_str(char *srcp, char *mntopts) +{ + char *key, *val, *p; + char tmpbuf[MAX_MNTOPT_STR]; + char *tbp = tmpbuf; + + (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]; + + 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); + } else if (strcmp(key, "clientaddr") == 0) { + /* + * Ignore, this is an artifact of the + * user-level lx mount code. + */ + } else if (strcmp(key, "vers") == 0) { + /* + * This should always be passed and is the + * only vers we want to support. + */ + if (atoi(val) != 4) + return (-EINVAL); + } 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, val); + if (r != 0) + return (r); + } + } + + return (0); +} + +int +lx_nfs_mount(char *srcp, char *mntp, char *fst, int lx_flags, char *opts) +{ + struct mnttab mnt; + int r; + int il_flags = 0; + err_ret_t retry_error; + nfs_mnt_data_t nmd; + + _nsl_brand_set_hooks(lx_nsl_set_sz_func, lx_get_ent_func); + + bzero(&nmd, sizeof (nmd)); + nmd.nmd_retries = BIGRETRY; + 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. Thus the rest of the code down the mount_nfs path will + * be working with that IP address in places were it references the + * 'hostname'. This also converts the opts string so that we'll be + * dealing with Illumos options after this. + */ + convert_nfs_arg_str(srcp, opts); + + /* 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); + + mnt.mnt_special = srcp; + mnt.mnt_mountp = mntp; + mnt.mnt_mntopts = opts; + + SET_ERR_RET(&retry_error, ERR_PROTO_NONE, 0); + r = mount_nfs(&mnt, il_flags, &retry_error, &nmd); + + /* A negative errno return means we're done. */ + if (r < 0) + return (r); + + if (r == RET_RETRY && nmd.nmd_retries) { + /* + * Check the error code from the last mount attempt. If it was + * an RPC error, then retry as is. Otherwise we retry with the + * nmd_nfsretry_vers set. It is set by decrementing + * nmd_nfsvers_to_use. + */ + if (retry_error.error_type) { + if (retry_error.error_type != ERR_RPCERROR) { + nmd.nmd_nfsretry_vers = + nmd.nmd_nfsvers_to_use = + nmd.nmd_nfsvers_to_use - 1; + if (nmd.nmd_nfsretry_vers < NFS_VERSMIN) + return (-EAGAIN); + } + } + + r = retry(&mnt, il_flags, &nmd); + } + + /* Convert any positve error into a valid errno. */ + if (r > 0) + return (-EAGAIN); + + return (0); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/pgrp.c b/usr/src/lib/brand/lx/lx_brand/common/pgrp.c new file mode 100644 index 0000000000..33b4bb7667 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/pgrp.c @@ -0,0 +1,172 @@ +/* + * 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 <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> + +long +lx_getpgrp(void) +{ + int ret; + + ret = getpgrp(); + + /* + * If the pgrp is that of the init process, return the value Linux + * expects. + */ + if (ret == zoneinit_pid) + return (LX_INIT_PGID); + + return ((ret == -1) ? -errno : ret); +} + +long +lx_getpgid(uintptr_t p1) +{ + pid_t spid; + int pid = (int)p1; + int ret; + + if (pid < 0) + return (-ESRCH); + + /* + * If the supplied pid matches that of the init process, return + * the pgid Linux expects. + */ + if (pid == zoneinit_pid) + return (LX_INIT_PGID); + + if ((ret = lx_lpid_to_spid(pid, &spid)) < 0) + return (ret); + + ret = getpgid(spid); + + /* + * If the pgid is that of the init process, return the value Linux + * expects. + */ + if (ret == zoneinit_pid) + return (LX_INIT_PGID); + + return ((ret == -1) ? -errno : ret); +} + +long +lx_setpgid(uintptr_t p1, uintptr_t p2) +{ + pid_t pid = (pid_t)p1; + pid_t pgid = (pid_t)p2; + pid_t spid, spgid; + int ret; + + if (pid < 0) + return (-ESRCH); + + if (pgid < 0) + return (-EINVAL); + + if ((ret = lx_lpid_to_spid(pid, &spid)) < 0) + return (ret); + + if (pgid == 0) + spgid = spid; + else if ((ret = lx_lpid_to_spid(pgid, &spgid)) < 0) + return (ret); + + ret = setpgid(spid, spgid); + + if (ret != 0 && errno == EPERM) { + /* + * On Linux, calling setpgid with a desired pgid that is equal + * to the current pgid of the process, no error is emitted. + * This differs slightly from illumos which will return EPERM. + * + * To emulate the Linux behavior, we check specifically for + * matching pgids if an EPERM is encountered. + */ + if (spgid == getpgid(spid)) + return (0); + else + return (-EPERM); + } + + return ((ret == 0) ? 0 : -errno); +} + +long +lx_getsid(uintptr_t p1) +{ + pid_t spid; + int pid = (int)p1; + int ret; + + if (pid < 0) + return (-ESRCH); + + /* + * If the supplied matches that of the init process, return the value + * Linux expects. + */ + if (pid == zoneinit_pid) + return (LX_INIT_SID); + + if ((ret = lx_lpid_to_spid(pid, &spid)) < 0) + return (ret); + + ret = getsid(spid); + + /* + * If the sid is that of the init process, return the value Linux + * expects. + */ + if (ret == zoneinit_pid) + return (LX_INIT_SID); + + return ((ret == -1) ? -errno : ret); +} + +long +lx_setsid(void) +{ + int ret; + + ret = setsid(); + + /* + * If the pgid is that of the init process, return the value Linux + * expects. + */ + if (ret == zoneinit_pid) + return (LX_INIT_SID); + + return ((ret == -1) ? -errno : ret); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/poll_select.c b/usr/src/lib/brand/lx/lx_brand/common/poll_select.c new file mode 100644 index 0000000000..029c59e4fb --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/poll_select.c @@ -0,0 +1,322 @@ +/* + * 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 (c) 2015, Joyent, Inc. All rights reserved. + */ + +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <strings.h> +#include <sys/param.h> +#include <sys/brand.h> +#include <sys/poll.h> +#include <sys/syscall.h> +#include <sys/epoll.h> +#include <sys/lx_debug.h> +#include <sys/lx_poll.h> +#include <sys/lx_syscall.h> +#include <sys/lx_brand.h> +#include <sys/lx_misc.h> + +#if defined(_ILP32) +extern int select_large_fdset(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0, + struct timeval *tv); +#endif + +long +lx_select(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5) +{ + int nfds = (int)p1; + fd_set *rfdsp = NULL; + fd_set *wfdsp = NULL; + fd_set *efdsp = NULL; + struct timeval tv, *tvp = NULL; + int fd_set_len = howmany(nfds, 8); + int r; + int res; + hrtime_t start = NULL, end; + + lx_debug("\tselect(%d, 0x%p, 0x%p, 0x%p, 0x%p)", p1, p2, p3, p4, p5); + + if (nfds > 0) { + if (p2 != NULL) { + rfdsp = malloc(fd_set_len); + if (rfdsp == NULL) { + res = -ENOMEM; + goto err; + } + if (uucopy((void *)p2, rfdsp, fd_set_len) != 0) { + res = -errno; + goto err; + } + } + if (p3 != NULL) { + wfdsp = malloc(fd_set_len); + if (wfdsp == NULL) { + res = -ENOMEM; + goto err; + } + if (uucopy((void *)p3, wfdsp, fd_set_len) != 0) { + res = -errno; + goto err; + } + } + if (p4 != NULL) { + efdsp = malloc(fd_set_len); + if (efdsp == NULL) { + res = -ENOMEM; + goto err; + } + if (uucopy((void *)p4, efdsp, fd_set_len) != 0) { + res = -errno; + goto err; + } + } + } + if (p5 != NULL) { + tvp = &tv; + if (uucopy((void *)p5, &tv, sizeof (tv)) != 0) { + res = -errno; + goto err; + } + start = gethrtime(); + } + +#if defined(_LP64) + r = select(nfds, rfdsp, wfdsp, efdsp, tvp); +#else + if (nfds >= FD_SETSIZE) + r = select_large_fdset(nfds, rfdsp, wfdsp, efdsp, tvp); + else + r = select(nfds, rfdsp, wfdsp, efdsp, tvp); +#endif + if (r < 0) { + res = -errno; + goto err; + } + + if (tvp != NULL) { + long long tv_total; + + /* + * Linux updates the timeval parameter for select() calls + * with the amount of time that left before the select + * would have timed out. + */ + end = gethrtime(); + tv_total = (tv.tv_sec * MICROSEC) + tv.tv_usec; + tv_total -= ((end - start) / (NANOSEC / MICROSEC)); + if (tv_total < 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = tv_total / MICROSEC; + tv.tv_usec = tv_total % MICROSEC; + } + + if (uucopy(&tv, (void *)p5, sizeof (tv)) != 0) { + res = -errno; + goto err; + } + } + + if ((rfdsp != NULL) && (uucopy(rfdsp, (void *)p2, fd_set_len) != 0)) { + res = -errno; + goto err; + } + if ((wfdsp != NULL) && (uucopy(wfdsp, (void *)p3, fd_set_len) != 0)) { + res = -errno; + goto err; + } + if ((efdsp != NULL) && (uucopy(efdsp, (void *)p4, fd_set_len) != 0)) { + res = -errno; + goto err; + } + + res = r; + +err: + if (rfdsp != NULL) + free(rfdsp); + if (wfdsp != NULL) + free(wfdsp); + if (efdsp != NULL) + free(efdsp); + return (res); +} + +long +lx_poll(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + struct pollfd *lfds = NULL; + struct pollfd *sfds = NULL; + nfds_t nfds = (nfds_t)p2; + int fds_size, i, rval, revents, res; + + /* + * Little emulation is needed if nfds == 0. + * If p1 happens to be NULL, it'll be dealt with later. + */ + if (nfds == 0) { + if ((rval = poll(NULL, 0, (int)p3)) < 0) + return (-errno); + + return (rval); + } + + /* + * Note: we are assuming that the Linux and Illumos pollfd + * structures are identical. Copy in the linux poll structure. + */ + fds_size = sizeof (struct pollfd) * nfds; + lfds = (struct pollfd *)malloc(fds_size); + if (lfds == NULL) { + res = -ENOMEM; + goto err; + } + if (uucopy((void *)p1, lfds, fds_size) != 0) { + res = -errno; + goto err; + } + + /* + * The poll system call modifies the poll structures passed in + * so we'll need to make an extra copy of them. + */ + sfds = (struct pollfd *)malloc(fds_size); + if (sfds == NULL) { + res = -ENOMEM; + goto err; + } + + /* Convert the Linux events bitmask into the Illumos equivalent. */ + for (i = 0; i < nfds; i++) { + /* + * If the caller is polling for an unsupported event, we + * have to bail out. + */ + if (lfds[i].events & ~LX_POLL_SUPPORTED_EVENTS) { + lx_unsupported("unsupported poll events requested: " + "events=0x%x", lfds[i].events); + res = -ENOTSUP; + goto err; + } + + sfds[i].fd = lfds[i].fd; + sfds[i].events = lfds[i].events & LX_POLL_COMMON_EVENTS; + if (lfds[i].events & LX_POLLWRNORM) + sfds[i].events |= POLLWRNORM; + if (lfds[i].events & LX_POLLWRBAND) + sfds[i].events |= POLLWRBAND; + if (lfds[i].events & LX_POLLRDHUP) + sfds[i].events |= POLLRDHUP; + sfds[i].revents = 0; + } + + lx_debug("\tpoll(0x%p, %u, %d)", sfds, nfds, (int)p3); + + if ((rval = poll(sfds, nfds, (int)p3)) < 0) { + res = -errno; + goto err; + } + + /* Convert the Illumos revents bitmask into the Linux equivalent */ + for (i = 0; i < nfds; i++) { + revents = sfds[i].revents & LX_POLL_COMMON_EVENTS; + if (sfds[i].revents & POLLWRBAND) + revents |= LX_POLLWRBAND; + if (sfds[i].revents & POLLRDHUP) + revents |= LX_POLLRDHUP; + + /* + * Be careful because on Illumos POLLOUT and POLLWRNORM + * are defined to the same values but on Linux they + * are not. + */ + if (sfds[i].revents & POLLOUT) { + if ((lfds[i].events & LX_POLLOUT) == 0) + revents &= ~LX_POLLOUT; + if (lfds[i].events & LX_POLLWRNORM) + revents |= LX_POLLWRNORM; + } + + lfds[i].revents = revents; + } + + /* Copy out the results */ + if (uucopy(lfds, (void *)p1, fds_size) != 0) { + res = -errno; + goto err; + } + + res = rval; + +err: + if (lfds != NULL) + free(lfds); + if (sfds != NULL) + free(sfds); + return (res); +} + +long +lx_epoll_ctl(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int epfd = (int)p1; + int op = (int)p2; + int fd = (int)p3; + int rval; + struct epoll_event ev; + + /* + * Linux limits the max number of open files to 1m so we can also test + * for this. + */ + if (epfd < 0 || fd < 0 || epfd > (1024 * 1024) || fd > (1024 * 1024)) + return (-EBADF); + + if (epfd == fd) + return (-EINVAL); + + /* + * Unlike the base epoll_ctl, we need to return a fault if the + * event pointer is invalid. + */ + if (op != EPOLL_CTL_DEL) { + if (uucopy((void *)p4, &ev, sizeof (ev)) != 0) + return (-errno); + } + + rval = epoll_ctl(epfd, op, fd, (struct epoll_event *)p4); + return ((rval < 0) ? -errno : rval); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/priority.c b/usr/src/lib/brand/lx/lx_brand/common/priority.c new file mode 100644 index 0000000000..48b61d6bdc --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/priority.c @@ -0,0 +1,95 @@ +/* + * 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 <sys/types.h> +#include <sys/lx_debug.h> +#include <sys/lx_misc.h> +#include <sys/lx_syscall.h> +#include <sys/lx_types.h> +#include <sys/resource.h> +#include <sys/lx_misc.h> + +long +lx_getpriority(uintptr_t p1, uintptr_t p2) +{ + int which = (int)p1; + id_t who = (id_t)p2; + int ret; + + /* + * The only valid values for 'which' are positive integers, and unlike + * Solaris, linux doesn't support anything past PRIO_USER. + */ + if (which < 0 || which > PRIO_USER) + return (-EINVAL); + + lx_debug("\tgetpriority(%d, %d)", which, who); + + errno = 0; + + if ((which == PRIO_PROCESS) && (who == 1)) + who = zoneinit_pid; + + ret = getpriority(which, who); + if (ret == -1 && errno != 0) { + /* + * Linux does not return EINVAL for invalid 'who' values, it + * returns ESRCH instead. We already validated 'which' above. + */ + if (errno == EINVAL) + errno = ESRCH; + return (-errno); + } + + /* + * The return value of the getpriority syscall is biased by 20 to avoid + * returning negative values when successful. + */ + return (20 - ret); +} + +long +lx_setpriority(uintptr_t p1, uintptr_t p2, uintptr_t p3) +{ + int which = (int)p1; + id_t who = (id_t)p2; + int prio = (int)p3; + int rval; + + if (which > PRIO_USER) + return (-EINVAL); + + lx_debug("\tsetpriority(%d, %d, %d)", which, who, prio); + + if ((which == PRIO_PROCESS) && (who == 1)) + who = zoneinit_pid; + + rval = setpriority(which, who, prio); + + return ((rval == -1) ? -errno : rval); +} 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..5cdbf1da87 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c @@ -0,0 +1,889 @@ +/* + * 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. + */ + +/* + * This corresponds to the user_i387_struct Linux structure. + */ +typedef struct lx_user_fpregs { + long lxuf_cwd; + long lxuf_swd; + long lxuf_twd; + long lxuf_fip; + long lxuf_fcs; + long lxuf_foo; + long lxuf_fos; + long lxuf_st_space[20]; +} lx_user_fpregs_t; + +/* + * This corresponds to the user_fxsr_struct Linux structure. + */ +typedef struct lx_user_fpxregs { + uint16_t lxux_cwd; + uint16_t lxux_swd; + uint16_t lxux_twd; + uint16_t lxux_fop; + long lxux_fip; + long lxux_fcs; + long lxux_foo; + long lxux_fos; + long lxux_mxcsr; + long lxux_reserved; + long lxux_st_space[32]; + long lxux_xmm_space[32]; + long lxux_padding[56]; +} lx_user_fpxregs_t; + +typedef struct lx_user { + lx_user_regs_t lxu_regs; + int lxu_fpvalid; + lx_user_fpregs_t lxu_i387; + ulong_t lxu_tsize; + ulong_t lxu_dsize; + ulong_t lxu_ssize; + ulong_t lxu_start_code; + ulong_t lxu_start_stack; + long lxu_signal; + int lxu_reserved; + lx_user_regs_t *lxu_ar0; + lx_user_fpregs_t *lxu_fpstate; + ulong_t lxu_magic; + char lxu_comm[32]; + int lxu_debugreg[8]; +} lx_user_t; + +typedef struct ptrace_state_map { + struct ptrace_state_map *psm_next; /* next pointer */ + pid_t psm_pid; /* Solaris pid */ + uintptr_t psm_debugreg[8]; /* debug registers */ +} ptrace_state_map_t; + +static ptrace_state_map_t *ptrace_state_map = NULL; +static mutex_t ptrace_map_mtx = DEFAULTMUTEX; + +extern void *_START_; + +static sigset_t blockable_sigs; + +static long lx_ptrace_kernel(int, pid_t, uintptr_t, uintptr_t); + +void +lx_ptrace_init(void) +{ + (void) sigfillset(&blockable_sigs); + (void) sigdelset(&blockable_sigs, SIGKILL); + (void) sigdelset(&blockable_sigs, SIGSTOP); +} + +/* + * Given a pid, open the named file under /native/proc/<pid>/name using the + * given mode. + */ +static int +open_procfile(pid_t pid, int mode, const char *name) +{ + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), "/native/proc/%d/%s", pid, name); + + return (open(path, mode)); +} + +/* + * Given a pid and lwpid, open the named file under + * /native/proc/<pid>/<lwpid>/name using the given mode. + */ +static int +open_lwpfile(pid_t pid, lwpid_t lwpid, int mode, const char *name) +{ + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), "/native/proc/%d/lwp/%d/%s", + pid, lwpid, name); + + return (open(path, mode)); +} + +static int +get_lwpstatus(pid_t pid, lwpid_t lwpid, lwpstatus_t *lsp) +{ + int fd; + + if ((fd = open_lwpfile(pid, lwpid, O_RDONLY, "lwpstatus")) < 0) + return (-ESRCH); + + if (read(fd, lsp, sizeof (lwpstatus_t)) != sizeof (lwpstatus_t)) { + (void) close(fd); + return (-EIO); + } + + (void) close(fd); + + return (0); +} + +static int +getfpregs(pid_t pid, lwpid_t lwpid, lx_user_fpregs_t *rp) +{ + lwpstatus_t status; + struct _fpstate *fp; +#if defined(_ILP32) + char *data; + int i; +#endif + int ret; + + if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) + return (ret); + + fp = (struct _fpstate *)&status.pr_fpreg.fp_reg_set.fpchip_state; + +#if defined(_ILP32) + rp->lxuf_cwd = fp->cw; + rp->lxuf_swd = fp->sw; + rp->lxuf_twd = fp->tag; + rp->lxuf_fip = fp->ipoff; + rp->lxuf_fcs = fp->cssel; + rp->lxuf_foo = fp->dataoff; + rp->lxuf_fos = fp->datasel; + + /* + * The Linux structure uses 10 bytes per floating-point register. + */ + data = (char *)&rp->lxuf_st_space[0]; + for (i = 0; i < 8; i++) { + bcopy(&fp->_st[i], data, 10); + data += 10; + } +#endif + + return (0); +} + +static int +setfpregs(pid_t pid, lwpid_t lwpid, const lx_user_fpregs_t *rp) +{ + lwpstatus_t status; + struct { + long cmd; + prfpregset_t regs; + } ctl; +#if defined(_ILP32) + struct _fpstate *fp = (struct _fpstate *)&ctl.regs; + char *data; + int i; +#endif + int ret, fd; + + if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) + return (ret); + + bcopy(&status.pr_fpreg, &ctl.regs, sizeof (ctl.regs)); + +#if defined(_ILP32) + fp->cw = rp->lxuf_cwd; + fp->sw = rp->lxuf_swd; + fp->tag = rp->lxuf_twd; + fp->ipoff = rp->lxuf_fip; + fp->cssel = rp->lxuf_fcs; + fp->dataoff = rp->lxuf_foo; + fp->datasel = rp->lxuf_fos; + + /* + * The Linux structure uses 10 bytes per floating-point register. + */ + data = (char *)&rp->lxuf_st_space[0]; + for (i = 0; i < 8; i++) { + bcopy(data, &fp->_st[i], 10); + data += 10; + } +#endif + + if ((fd = open_lwpfile(pid, lwpid, O_WRONLY, "lwpctl")) < 0) + return (-ESRCH); + + ctl.cmd = PCSFPREG; + if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { + (void) close(fd); + return (-EIO); + } + + (void) close(fd); + + return (0); +} + + +static int +getfpxregs(pid_t pid, lwpid_t lwpid, lx_user_fpxregs_t *rp) +{ +#if defined(_ILP32) + lwpstatus_t status; + struct _fpstate *fp; + int ret, i; + + if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) + return (ret); + + fp = (struct _fpstate *)&status.pr_fpreg.fp_reg_set.fpchip_state; + + rp->lxux_cwd = (uint16_t)fp->cw; + rp->lxux_swd = (uint16_t)fp->sw; + rp->lxux_twd = (uint16_t)fp->tag; + rp->lxux_fop = (uint16_t)(fp->cssel >> 16); + rp->lxux_fip = fp->ipoff; + rp->lxux_fcs = (uint16_t)fp->cssel; + rp->lxux_foo = fp->dataoff; + rp->lxux_fos = fp->datasel; + rp->lxux_mxcsr = status.pr_fpreg.fp_reg_set.fpchip_state.mxcsr; + + bcopy(fp->xmm, rp->lxux_xmm_space, sizeof (rp->lxux_xmm_space)); + bzero(rp->lxux_st_space, sizeof (rp->lxux_st_space)); + for (i = 0; i < 8; i++) { + bcopy(&fp->_st[i], &rp->lxux_st_space[i * 4], + sizeof (fp->_st[i])); + } +#endif + + return (0); +} + +static int +setfpxregs(pid_t pid, lwpid_t lwpid, const lx_user_fpxregs_t *rp) +{ +#if defined(_ILP32) + lwpstatus_t status; + struct { + long cmd; + prfpregset_t regs; + } ctl; + struct _fpstate *fp = (struct _fpstate *)&ctl.regs; + int ret, i, fd; + + if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) + return (ret); + + bcopy(&status.pr_fpreg, &ctl.regs, sizeof (ctl.regs)); + + fp->cw = rp->lxux_cwd; + fp->sw = rp->lxux_swd; + fp->tag = rp->lxux_twd; + fp->ipoff = rp->lxux_fip; + fp->cssel = rp->lxux_fcs | (rp->lxux_fop << 16); + fp->dataoff = rp->lxux_foo; + fp->datasel = rp->lxux_fos; + + bcopy(rp->lxux_xmm_space, fp->xmm, sizeof (rp->lxux_xmm_space)); + for (i = 0; i < 8; i++) { + bcopy(&rp->lxux_st_space[i * 4], &fp->_st[i], + sizeof (fp->_st[i])); + } + + if ((fd = open_lwpfile(pid, lwpid, O_WRONLY, "lwpctl")) < 0) + return (-ESRCH); + + ctl.cmd = PCSFPREG; + if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { + (void) close(fd); + return (-EIO); + } + + (void) close(fd); +#endif + + return (0); +} + +/* + * Solaris does not allow a process to manipulate its own or some + * other process's debug registers. Linux ptrace(2) allows this + * and gdb manipulates them for its watchpoint implementation. + * + * We keep a pseudo set of debug registers for each traced process + * and map their contents into the appropriate PCWATCH /proc + * operations when they are activated by gdb. + * + * To understand how the debug registers work on x86 machines, + * see section 13.1 of the AMD x86-64 Architecture Programmer's + * Manual, Volume 2, System Programming. + */ +static uintptr_t * +debug_registers(pid_t pid) +{ + ptrace_state_map_t *p; + + (void) mutex_lock(&ptrace_map_mtx); + for (p = ptrace_state_map; p != NULL; p = p->psm_next) { + if (p->psm_pid == pid) + break; + } + if (p == NULL && (p = malloc(sizeof (*p))) != NULL) { + bzero(p, sizeof (*p)); + p->psm_pid = pid; + p->psm_next = ptrace_state_map; + p->psm_debugreg[6] = 0xffff0ff0; /* read as ones */ + ptrace_state_map = p; + } + (void) mutex_unlock(&ptrace_map_mtx); + return (p != NULL? p->psm_debugreg : NULL); +} + +static int +setup_watchpoints(pid_t pid, uintptr_t *debugreg) +{ + int dr7 = debugreg[7]; + int lrw; + int fd; + size_t size = NULL; + prwatch_t prwatch[4]; + int nwatch; + int i; + int wflags = NULL; + int error; + struct { + long req; + prwatch_t prwatch; + } ctl; + + /* find all watched areas */ + if ((fd = open_procfile(pid, O_RDONLY, "watch")) < 0) + return (-ESRCH); + nwatch = read(fd, prwatch, sizeof (prwatch)) / sizeof (prwatch_t); + (void) close(fd); + if ((fd = open_procfile(pid, O_WRONLY, "ctl")) < 0) + return (-ESRCH); + /* clear all watched areas */ + for (i = 0; i < nwatch; i++) { + ctl.req = PCWATCH; + ctl.prwatch = prwatch[i]; + ctl.prwatch.pr_wflags = 0; + if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { + error = -errno; + (void) close(fd); + return (error); + } + } + /* establish all new watched areas */ + for (i = 0; i < 4; i++) { + if ((dr7 & (1 << (2 * i))) == 0) /* enabled? */ + continue; + lrw = (dr7 >> (16 + (4 * i))) & 0xf; + switch (lrw >> 2) { /* length */ + case 0: size = 1; break; + case 1: size = 2; break; + case 2: size = 8; break; + case 3: size = 4; break; + } + switch (lrw & 0x3) { /* mode */ + case 0: wflags = WA_EXEC; break; + case 1: wflags = WA_WRITE; break; + case 2: continue; + case 3: wflags = WA_READ | WA_WRITE; break; + } + ctl.req = PCWATCH; + ctl.prwatch.pr_vaddr = debugreg[i]; + ctl.prwatch.pr_size = size; + ctl.prwatch.pr_wflags = wflags | WA_TRAPAFTER; + if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { + error = -errno; + (void) close(fd); + return (error); + } + } + (void) close(fd); + return (0); +} + +/* + * Returns B_TRUE if the target LWP, identified by its Linux pid, is traced by + * this LWP and is waiting in "ptrace-stop". Returns B_FALSE otherwise. + */ +static boolean_t +is_ptrace_stopped(pid_t lxpid) +{ + ulong_t dummy; + + /* + * We attempt a PTRACE_GETEVENTMSG request to determine if the tracee + * is stopped appropriately. As we are not in the kernel, this is not + * an atomic check; the process is not guaranteed to remain stopped + * once we have dropped the locks protecting that state and left the + * kernel. + */ + if (lx_ptrace_kernel(LX_PTRACE_GETEVENTMSG, lxpid, NULL, + (uintptr_t)&dummy) == 0) { + return (B_TRUE); + } + + /* + * This call should only fail with ESRCH, which tells us that the + * a tracee with that pid was not found in the stopped condition. + */ + assert(errno == ESRCH); + + return (B_FALSE); +} + +/* + * Read a word of data from the given address. Because this is a process-wide + * action, we don't need the lwpid. + */ +static long +ptrace_peek(pid_t pid, uintptr_t addr, long *ret) +{ + int fd; + long data; + + if ((fd = open_procfile(pid, O_RDONLY, "as")) < 0) + return (-ESRCH); + + if (pread(fd, &data, sizeof (data), addr) != sizeof (data)) { + (void) close(fd); + return (-EIO); + } + + (void) close(fd); + + if (uucopy(&data, ret, sizeof (data)) != 0) + return (-errno); + + return (0); +} + +#define LX_USER_BOUND(m) \ +(offsetof(lx_user_t, m) + sizeof (((lx_user_t *)NULL)->m)) + +static int +ptrace_peek_user(pid_t lxpid, pid_t pid, lwpid_t lwpid, uintptr_t off, int *ret) +{ + int err; + long data; + uintptr_t *debugreg; + int dreg; + + /* + * The offset specified by the user is an offset into the Linux + * user structure (seriously). Rather than constructing a full + * user structure, we figure out which part of the user structure + * the offset is in, and fill in just that component. + */ + if (off < LX_USER_BOUND(lxu_regs)) { + lx_user_regs_t regs; + + if ((err = lx_ptrace_kernel(LX_PTRACE_GETREGS, lxpid, NULL, + (uintptr_t)®s)) != 0) { + return (err); + } + + data = *(long *)((uintptr_t)®s + off - + offsetof(lx_user_t, lxu_regs)); + + } else if (off < LX_USER_BOUND(lxu_fpvalid)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_i387)) { + lx_user_fpregs_t regs; + + if ((err = getfpregs(pid, lwpid, ®s)) != 0) + return (err); + + data = *(long *)((uintptr_t)®s + off - + offsetof(lx_user_t, lxu_i387)); + + } else if (off < LX_USER_BOUND(lxu_tsize)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_dsize)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_ssize)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_start_code)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_start_stack)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_signal)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_reserved)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_ar0)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_fpstate)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_magic)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_comm)) { + lx_err("offset = %lu\n", off); + assert(0); + } else if (off < LX_USER_BOUND(lxu_debugreg)) { + dreg = (off - offsetof(lx_user_t, lxu_debugreg)) / sizeof (int); + if (dreg == 4) /* aliased */ + dreg = 6; + else if (dreg == 5) /* aliased */ + dreg = 7; + if ((debugreg = debug_registers(pid)) != NULL) + data = debugreg[dreg]; + else + data = 0; + } else { + lx_unsupported("unsupported ptrace peek user offset: 0x%x\n", + off); + assert(0); + return (-ENOTSUP); + } + + if (uucopy(&data, ret, sizeof (data)) != 0) + return (-errno); + + return (0); +} + +/* + * Write a word of data to the given address. Because this is a process-wide + * action, we don't need the lwpid. Returns EINVAL if the address is not + * word-aligned. + */ +static int +ptrace_poke(pid_t pid, uintptr_t addr, int data) +{ + int fd; + + if (addr & 0x3) + return (-EINVAL); + + if ((fd = open_procfile(pid, O_WRONLY, "as")) < 0) + return (-ESRCH); + + if (pwrite(fd, &data, sizeof (data), addr) != sizeof (data)) { + (void) close(fd); + return (-EIO); + } + + (void) close(fd); + return (0); +} + +static int +ptrace_poke_user(pid_t lxpid, pid_t pid, lwpid_t lwpid, uintptr_t off, int data) +{ + lx_user_regs_t regs; + int err = 0; + uintptr_t *debugreg; + int dreg; + + if (off & 0x3) + return (-EINVAL); + + if (off < offsetof(lx_user_t, lxu_regs) + sizeof (lx_user_regs_t)) { + if ((err = lx_ptrace_kernel(LX_PTRACE_GETREGS, lxpid, NULL, + (uintptr_t)®s)) != 0) { + return (err); + } + + *(int *)((uintptr_t)®s + off - + offsetof(lx_user_t, lxu_regs)) = data; + + return (lx_ptrace_kernel(LX_PTRACE_SETREGS, lxpid, NULL, + (uintptr_t)®s)); + } + + if (off >= offsetof(lx_user_t, lxu_debugreg) && + off < offsetof(lx_user_t, lxu_debugreg) + 8 * sizeof (int)) { + dreg = (off - offsetof(lx_user_t, lxu_debugreg)) / sizeof (int); + if (dreg == 4) /* aliased */ + dreg = 6; + else if (dreg == 5) /* aliased */ + dreg = 7; + if ((debugreg = debug_registers(pid)) != NULL) { + debugreg[dreg] = data; + if (dreg == 7) + err = setup_watchpoints(pid, debugreg); + } + return (err); + } + + lx_unsupported("unsupported ptrace poke user offset: 0x%x\n", off); + assert(0); + return (-ENOTSUP); +} + +static int +ptrace_kill(pid_t pid) +{ + int ret; + + ret = kill(pid, SIGKILL); + + return (ret == 0 ? ret : -errno); +} + +static int +ptrace_getfpregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) +{ + lx_user_fpregs_t regs; + int ret; + + if ((ret = getfpregs(pid, lwpid, ®s)) != 0) + return (ret); + + if (uucopy(®s, (void *)addr, sizeof (regs)) != 0) + return (-errno); + + return (0); +} + +static int +ptrace_setfpregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) +{ + lx_user_fpregs_t regs; + + if (uucopy((void *)addr, ®s, sizeof (regs)) != 0) + return (-errno); + + return (setfpregs(pid, lwpid, ®s)); +} + +static int +ptrace_getfpxregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) +{ + lx_user_fpxregs_t regs; + int ret; + + if ((ret = getfpxregs(pid, lwpid, ®s)) != 0) + return (ret); + + if (uucopy(®s, (void *)addr, sizeof (regs)) != 0) + return (-errno); + + return (0); +} + +static int +ptrace_setfpxregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) +{ + lx_user_fpxregs_t regs; + + if (uucopy((void *)addr, ®s, sizeof (regs)) != 0) + return (-errno); + + return (setfpxregs(pid, lwpid, ®s)); +} + +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) +{ + lx_debug("lx_ptrace_clone_begin(%d, %sPTRACE_CLONE)", option, + inherit_flag ? "" : "!"); + if (syscall(SYS_brand, B_PTRACE_CLONE_BEGIN, option, + inherit_flag) != 0) { + lx_err_fatal("B_PTRACE_CLONE_BEGIN failed: %s", + strerror(errno)); + } +} + +static long +lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) +{ + int ret; + + /* + * Call into the in-kernel ptrace(2) emulation code. + */ + lx_debug("revectoring to B_PTRACE_KERNEL(%d, %d, %p, %p)", ptrace_op, + lxpid, addr, data); + ret = syscall(SYS_brand, B_PTRACE_KERNEL, ptrace_op, lxpid, addr, + data); + if (ret == 0) { + lx_debug("\t= %d", ret); + } else { + lx_debug("\t= %d (%s)", ret, strerror(errno)); + } + + return (ret == 0 ? ret : -errno); +} + +long +lx_ptrace(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int ptrace_op = (int)p1; + pid_t pid, lxpid = (pid_t)p2; + lwpid_t lwpid; + + /* + * Some PTRACE_* requests are emulated entirely in the kernel. + */ + switch (ptrace_op) { + /* + * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of + * one LWP by another. The target LWP must not be traced already. + * Both `data' and `addr' are ignored in both cases. + */ + case LX_PTRACE_TRACEME: + return (lx_ptrace_kernel(ptrace_op, 0, 0, 0)); + + case LX_PTRACE_ATTACH: + return (lx_ptrace_kernel(ptrace_op, lxpid, 0, 0)); + + /* + * PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP and PTRACE_CONT + * are all restarting actions. They are only allowed when attached + * to the target LWP and when that target LWP is in a "ptrace-stop" + * condition. + */ + case LX_PTRACE_DETACH: + case LX_PTRACE_SYSCALL: + case LX_PTRACE_CONT: + case LX_PTRACE_SINGLESTEP: + /* + * These actions also require the LWP to be traced and stopped, but do + * not restart the target LWP. + */ + case LX_PTRACE_SETOPTIONS: + case LX_PTRACE_GETEVENTMSG: + case LX_PTRACE_GETREGS: + case LX_PTRACE_SETREGS: + return (lx_ptrace_kernel(ptrace_op, lxpid, p3, p4)); + } + + /* + * The rest of the emulated PTRACE_* actions are emulated in userland. + * They require the target LWP to be traced and in currently + * "ptrace-stop", but do not subsequently restart the target LWP. + */ + if (lx_lpid_to_spair(lxpid, &pid, &lwpid) < 0 || + !is_ptrace_stopped(lxpid)) { + return (-ESRCH); + } + + switch (ptrace_op) { + case LX_PTRACE_PEEKTEXT: + case LX_PTRACE_PEEKDATA: + return (ptrace_peek(pid, p3, (long *)p4)); + + case LX_PTRACE_PEEKUSER: + return (ptrace_peek_user(lxpid, pid, lwpid, p3, (int *)p4)); + + case LX_PTRACE_POKETEXT: + case LX_PTRACE_POKEDATA: + return (ptrace_poke(pid, p3, (int)p4)); + + case LX_PTRACE_POKEUSER: + return (ptrace_poke_user(lxpid, pid, lwpid, p3, (int)p4)); + + case LX_PTRACE_KILL: + return (ptrace_kill(pid)); + + case LX_PTRACE_GETFPREGS: + return (ptrace_getfpregs(pid, lwpid, p4)); + + case LX_PTRACE_SETFPREGS: + return (ptrace_setfpregs(pid, lwpid, p4)); + + case LX_PTRACE_GETFPXREGS: + return (ptrace_getfpxregs(pid, lwpid, p4)); + + case LX_PTRACE_SETFPXREGS: + return (ptrace_setfpxregs(pid, lwpid, p4)); + + default: + return (-EINVAL); + } +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/sched.c b/usr/src/lib/brand/lx/lx_brand/common/sched.c new file mode 100644 index 0000000000..f56f5c18e9 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/sched.c @@ -0,0 +1,624 @@ +/* + * 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 <sys/types.h> +#include <sys/cred_impl.h> +#include <sys/ucred.h> +#include <ucred.h> +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include <sched.h> +#include <strings.h> +#include <pthread.h> +#include <time.h> +#include <thread.h> +#include <alloca.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/lx_syscall.h> +#include <sys/lx_debug.h> +#include <sys/lx_brand.h> +#include <sys/lx_misc.h> +#include <sys/lx_sched.h> + +/* Linux only has three valid policies, SCHED_FIFO, SCHED_RR and SCHED_OTHER */ +static int +validate_policy(int policy) +{ + switch (policy) { + case LX_SCHED_FIFO: + return (SCHED_FIFO); + + case LX_SCHED_RR: + return (SCHED_RR); + + case LX_SCHED_OTHER: + return (SCHED_OTHER); + + default: + lx_debug("validate_policy: illegal policy: %d", policy); + return (-EINVAL); + } +} + +/* + * Check to see if we have the permissions to set scheduler parameters and + * policy, based on Linux' demand that such commands fail with errno set to + * EPERM if the current euid is not the euid or ruid of the process in + * question. + */ +static int +check_schedperms(pid_t pid) +{ + size_t sz; + ucred_t *cr; + uid_t euid; + + euid = geteuid(); + + if (pid == getpid()) { + /* + * If we're the process to be checked, simply check the euid + * against our ruid. + */ + if (euid != getuid()) + return (-EPERM); + + return (0); + } + + /* + * We allocate a ucred_t ourselves rather than call ucred_get(3C) + * because ucred_get() calls malloc(3C), which the brand library cannot + * use. Because we allocate the space with SAFE_ALLOCA(), there's + * no need to free it when we're done. + */ + sz = ucred_size(); + cr = (ucred_t *)SAFE_ALLOCA(sz); + + if (cr == NULL) + return (-ENOMEM); + + /* + * If we can't access the process' credentials, fail with errno EPERM + * as the call would not have succeeded anyway. + */ + if (syscall(SYS_ucredsys, UCREDSYS_UCREDGET, pid, cr) != 0) + return ((errno == EACCES) ? -EPERM : -errno); + + if ((euid != ucred_geteuid(cr)) && (euid != ucred_getruid(cr))) + return (-EPERM); + + return (0); +} + +static int +ltos_sparam(int policy, struct lx_sched_param *lsp, struct sched_param *sp) +{ + struct lx_sched_param ls; + int smin = sched_get_priority_min(policy); + int smax = sched_get_priority_max(policy); + + if (uucopy(lsp, &ls, sizeof (struct lx_sched_param)) != 0) + return (-errno); + + bzero(sp, sizeof (struct sched_param)); + + /* + * Linux has a fixed priority range, 0 - 99, which we need to convert to + * Solaris's dynamic range. Linux considers lower numbers to be + * higher priority, so we'll invert the priority within Solaris's range. + * + * The formula to convert between ranges is: + * + * L * (smax - smin) + * S = ----------------- + smin + * (lmax - lmin) + * + * where S is the Solaris equivalent of the linux priority L. + * + * To invert the priority, we use: + * S' = smax - S + smin + * + * Together, these two formulas become: + * + * L * (smax - smin) + * S = smax - ----------------- + 2smin + * 99 + */ + sp->sched_priority = smax - + ((ls.lx_sched_prio * (smax - smin)) / LX_PRI_MAX) + 2*smin; + + lx_debug("ltos_sparam: linux prio %d = Solaris prio %d " + "(Solaris range %d,%d)\n", ls.lx_sched_prio, sp->sched_priority, + smin, smax); + + return (0); +} + +static int +stol_sparam(int policy, struct sched_param *sp, struct lx_sched_param *lsp) +{ + struct lx_sched_param ls; + int smin = sched_get_priority_min(policy); + int smax = sched_get_priority_max(policy); + + if (policy == SCHED_OTHER) { + /* + * In Linux, the only valid SCHED_OTHER scheduler priority is 0 + */ + ls.lx_sched_prio = 0; + } else { + /* + * Convert Solaris's dynamic, inverted priority range to the + * fixed Linux range of 1 - 99. + * + * The formula is (see above): + * + * (smax - s + 2smin) * 99 + * l = ----------------------- + * smax - smin + */ + ls.lx_sched_prio = ((smax - sp->sched_priority + 2*smin) * + LX_PRI_MAX) / (smax - smin); + } + + lx_debug("stol_sparam: policy %d: Solaris prio %d = linux prio %d " + "(Solaris range %d,%d)\n", policy, + sp->sched_priority, ls.lx_sched_prio, smin, smax); + + return ((uucopy(&ls, lsp, sizeof (struct lx_sched_param)) != 0) + ? -errno : 0); +} + +#define BITINDEX(ind) (ind / (sizeof (uint_t) * 8)) +#define BITSHIFT(ind) (1 << (ind % (sizeof (uint_t) * 8))) + +/* ARGSUSED */ +long +lx_sched_getaffinity(uintptr_t pid, uintptr_t len, uintptr_t maskp) +{ + int sz; + uint_t *lmask, *zmask; + int i; + + sz = syscall(SYS_brand, B_GET_AFFINITY_MASK, pid, len, maskp); + if (sz == -1) + return (-errno); + + /* + * If the target LWP hasn't ever had an affinity mask set, the kernel + * will return a mask of all 0's. If that is the case we must build a + * default mask that has all valid bits turned on. + */ + lmask = SAFE_ALLOCA(sz); + zmask = SAFE_ALLOCA(sz); + if (lmask == NULL || zmask == NULL) + return (-ENOMEM); + + bzero(zmask, sz); + + if (uucopy((void *)maskp, lmask, sz) != 0) + return (-EFAULT); + + if (bcmp(lmask, zmask, sz) != 0) + return (sz); + + for (i = 0; i < sz * 8; i++) { + if (p_online(i, P_STATUS) != -1) { + lmask[BITINDEX(i)] |= BITSHIFT(i); + } + } + + if (uucopy(lmask, (void *)maskp, sz) != 0) + return (-EFAULT); + + return (sz); +} + +/* ARGSUSED */ +long +lx_sched_setaffinity(uintptr_t pid, uintptr_t len, uintptr_t maskp) +{ + int ret; + int sz; + int i; + int found; + uint_t *lmask; + pid_t s_pid; + lwpid_t s_tid; + processorid_t cpuid = NULL; + + if ((pid_t)pid < 0) + return (-EINVAL); + + if (lx_lpid_to_spair(pid, &s_pid, &s_tid) < 0) + return (-ESRCH); + + /* + * We only support setting affinity masks for threads in + * the calling process. + */ + if (s_pid != getpid()) + return (-EPERM); + + /* + * First, get the minimum bitmask size from the kernel. + */ + sz = syscall(SYS_brand, B_GET_AFFINITY_MASK, 0, 0, 0); + if (sz == -1) + return (-errno); + + lmask = SAFE_ALLOCA(sz); + if (lmask == NULL) + return (-ENOMEM); + + if (uucopy((void *)maskp, lmask, sz) != 0) + return (-EFAULT); + + /* + * Make sure the mask contains at least one processor that is + * physically on the system. Reduce the user's mask to the set of + * physically present CPUs. Keep track of how many valid + * bits are set in the user's mask. + */ + + for (found = 0, i = 0; i < sz * 8; i++) { + if (p_online(i, P_STATUS) == -1) { + /* + * This CPU doesn't exist, so clear this bit from + * the user's mask. + */ + lmask[BITINDEX(i)] &= ~BITSHIFT(i); + continue; + } + + if ((lmask[BITINDEX(i)] & BITSHIFT(i)) == BITSHIFT(i)) { + found++; + cpuid = i; + } + } + + if (found == 0) { + lx_debug("\tlx_sched_setaffinity: mask has no present CPUs\n"); + return (-EINVAL); + } + + /* + * If only one bit is set, bind the thread to that procesor; + * otherwise, clear the binding. + */ + if (found == 1) { + lx_debug("\tlx_sched_setaffinity: binding thread %d to cpu%d\n", + s_tid, cpuid); + if (processor_bind(P_LWPID, s_tid, cpuid, NULL) != 0) + /* + * It could be that the requested processor is offline, + * so we'll just abandon our good-natured attempt to + * bind to it. + */ + lx_debug("couldn't bind LWP %d to cpu %d: %s\n", s_tid, + cpuid, strerror(errno)); + } else { + lx_debug("\tlx_sched_setaffinity: clearing thr %d binding\n", + s_tid); + if (processor_bind(P_LWPID, s_tid, PBIND_NONE, NULL) != 0) { + lx_debug("couldn't clear CPU binding for LWP %d: %s\n", + s_tid, strerror(errno)); + } + } + + /* + * Finally, ask the kernel to make a note of our current (though fairly + * meaningless) affinity mask. + */ + ret = syscall(SYS_brand, B_SET_AFFINITY_MASK, pid, sz, lmask); + + return ((ret == 0) ? 0 : -errno); +} + +long +lx_sched_getparam(uintptr_t pid, uintptr_t param) +{ + int policy, ret; + pid_t s_pid; + lwpid_t s_tid; + + struct sched_param sp; + + if (((pid_t)pid < 0) || (param == NULL)) + return (-EINVAL); + + if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0) + return (-ESRCH); + + /* + * If we're attempting to get information on our own process, we can + * get data on a per-thread basis; if not, punt and use the specified + * pid. + */ + if (s_pid == getpid()) { + if ((ret = pthread_getschedparam(s_tid, &policy, &sp)) != 0) + return (-ret); + } else { + if (sched_getparam(s_pid, &sp) == -1) + return (-errno); + + if ((policy = sched_getscheduler(s_pid)) < 0) + return (-errno); + } + + /* + * Make sure that any non-SCHED_FIFO non-SCHED_RR scheduler is mapped + * onto SCHED_OTHER. + */ + if (policy != SCHED_FIFO && policy != SCHED_RR) + policy = SCHED_OTHER; + + return (stol_sparam(policy, &sp, (struct lx_sched_param *)param)); +} + +long +lx_sched_setparam(uintptr_t pid, uintptr_t param) +{ + int err, policy; + pid_t s_pid; + lwpid_t s_tid; + struct lx_sched_param lp; + struct sched_param sp; + + if (((pid_t)pid < 0) || (param == NULL)) + return (-EINVAL); + + if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0) + return (-ESRCH); + + if (s_pid == getpid()) { + struct sched_param dummy; + + if ((err = pthread_getschedparam(s_tid, &policy, &dummy)) != 0) + return (-err); + } else + if ((policy = sched_getscheduler(s_pid)) < 0) + return (-errno); + + lx_debug("sched_setparam(): current policy %d", policy); + + if (uucopy((void *)param, &lp, sizeof (lp)) != 0) + return (-errno); + + /* + * In Linux, the only valid SCHED_OTHER scheduler priority is 0 + */ + if ((policy == SCHED_OTHER) && (lp.lx_sched_prio != 0)) + return (-EINVAL); + + if ((err = ltos_sparam(policy, (struct lx_sched_param *)&lp, + &sp)) != 0) + return (err); + + /* + * Check if we're allowed to change the scheduler for the process. + * + * If we're operating on a thread, we can't just call + * pthread_setschedparam() because as all threads reside within a + * single Solaris process, Solaris will allow the modification + * + * If we're operating on a process, we can't just call sched_setparam() + * because Solaris will allow the call to succeed if the scheduler + * parameters do not differ from those being installed, but Linux wants + * the call to fail. + */ + if ((err = check_schedperms(s_pid)) != 0) + return (err); + + if (s_pid == getpid()) + return (((err = pthread_setschedparam(s_tid, policy, &sp)) != 0) + ? -err : 0); + + return ((sched_setparam(s_pid, &sp) == -1) ? -errno : 0); +} + +long +lx_sched_rr_get_interval(uintptr_t pid, uintptr_t timespec) +{ + struct timespec ts; + pid_t s_pid; + + if ((pid_t)pid < 0) + return (-EINVAL); + + if (lx_lpid_to_spid((pid_t)pid, &s_pid) < 0) + return (-ESRCH); + + if (uucopy((struct timespec *)timespec, &ts, + sizeof (struct timespec)) != 0) + return (-errno); + + return ((sched_rr_get_interval(s_pid, &ts) == -1) ? -errno : 0); +} + +long +lx_sched_getscheduler(uintptr_t pid) +{ + int policy, rv; + pid_t s_pid; + lwpid_t s_tid; + + if ((pid_t)pid < 0) + return (-EINVAL); + + if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0) + return (-ESRCH); + + if (s_pid == getpid()) { + struct sched_param dummy; + + if ((rv = pthread_getschedparam(s_tid, &policy, &dummy)) != 0) + return (-rv); + } else + if ((policy = sched_getscheduler(s_pid)) < 0) + return (-errno); + + /* + * Linux only supports certain policies; avoid confusing apps with + * alien policies. + */ + switch (policy) { + case SCHED_FIFO: + return (LX_SCHED_FIFO); + case SCHED_OTHER: + return (LX_SCHED_OTHER); + case SCHED_RR: + return (LX_SCHED_RR); + default: + break; + } + + return (LX_SCHED_OTHER); +} + +long +lx_sched_setscheduler(uintptr_t pid, uintptr_t policy, uintptr_t param) +{ + int rt_pol; + int rv; + pid_t s_pid; + lwpid_t s_tid; + struct lx_sched_param lp; + + struct sched_param sp; + + if (((pid_t)pid < 0) || (param == NULL)) + return (-EINVAL); + + if ((rt_pol = validate_policy((int)policy)) < 0) + return (rt_pol); + + if ((rv = ltos_sparam(policy, (struct lx_sched_param *)param, + &sp)) != 0) + return (rv); + + if (uucopy((void *)param, &lp, sizeof (lp)) != 0) + return (-errno); + + if (rt_pol == LX_SCHED_OTHER) { + /* + * In Linux, the only valid SCHED_OTHER scheduler priority is 0 + */ + if (lp.lx_sched_prio != 0) + return (-EINVAL); + + /* + * If we're already SCHED_OTHER, there's nothing else to do. + */ + if (lx_sched_getscheduler(pid) == LX_SCHED_OTHER) + return (0); + } + + if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0) + return (-ESRCH); + + /* + * Check if we're allowed to change the scheduler for the process. + * + * If we're operating on a thread, we can't just call + * pthread_setschedparam() because as all threads reside within a + * single Solaris process, Solaris will allow the modification. + * + * If we're operating on a process, we can't just call + * sched_setscheduler() because Solaris will allow the call to succeed + * if the scheduler and scheduler parameters do not differ from those + * being installed, but Linux wants the call to fail. + */ + if ((rv = check_schedperms(s_pid)) != 0) + return (rv); + + if (s_pid == getpid()) { + struct sched_param param; + int pol; + + if ((pol = sched_getscheduler(s_pid)) == -1) + return (-errno); + + /* + * sched_setscheduler() returns the previous scheduling policy + * on success, so call pthread_getschedparam() to get the + * current thread's scheduling policy and return that if the + * call to pthread_setschedparam() succeeds. + */ + if ((rv = pthread_getschedparam(s_tid, &pol, ¶m)) != 0) + return (-rv); + + return (((rv = pthread_setschedparam(s_tid, rt_pol, &sp)) != 0) + ? -rv : pol); + } + + return (((rv = sched_setscheduler(s_pid, rt_pol, &sp)) == -1) + ? -errno : rv); +} + +long +lx_sched_get_priority_min(uintptr_t policy) +{ + /* + * In Linux, the only valid SCHED_OTHER scheduler priority is 0. + * Linux scheduling priorities are not alterable, so there is no + * Solaris translation necessary. + */ + switch (policy) { + case LX_SCHED_FIFO: + case LX_SCHED_RR: + return (LX_SCHED_PRIORITY_MIN_RRFIFO); + case LX_SCHED_OTHER: + return (LX_SCHED_PRIORITY_MIN_OTHER); + default: + break; + } + return (-EINVAL); +} + +long +lx_sched_get_priority_max(uintptr_t policy) +{ + /* + * In Linux, the only valid SCHED_OTHER scheduler priority is 0 + * Linux scheduling priorities are not alterable, so there is no + * Solaris translation necessary. + */ + switch (policy) { + case LX_SCHED_FIFO: + case LX_SCHED_RR: + return (LX_SCHED_PRIORITY_MAX_RRFIFO); + case LX_SCHED_OTHER: + return (LX_SCHED_PRIORITY_MAX_OTHER); + default: + break; + } + return (-EINVAL); +} 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..319f51038f --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/sendfile.c @@ -0,0 +1,129 @@ +/* + * 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. + */ + +/* + * 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; + 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); + + if (error == 0 && xferred > 0) { + if (offp == NULL) { + /* If no offp, we must adjust current offset */ + if (lseek((int)p2, xferred, SEEK_CUR) == -1) + return (-errno); + } else { + off += xferred; + error = uucopy(&off, offp, sizeof (off)); + } + } + + return (error ? -error : (int)rval.sys_rval1); +} +#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); + + if (error == 0 && xferred > 0) { + if (offp == NULL) { + /* If no offp, we must adjust current offset */ + if (lseek((int)p2, xferred, SEEK_CUR) == -1) + return (-errno); + } else { + off += xferred; + error = uucopy(&off, offp, sizeof (off)); + } + } + + return (error ? -error : (int)rval.sys_rval1); +} 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..7dc5a04004 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/signal.c @@ -0,0 +1,2571 @@ +/* + * 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. All rights reserved. + */ + +#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/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> + +#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.) + * + * 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; + +/* + * Setting LX_NO_ABORT_HANDLER in the environment will prevent the emulated + * Linux program from modifying the signal handling disposition for SIGSEGV or + * SIGABRT. Useful for debugging programs which fall over themselves to + * prevent useful core files being generated. + */ +static int lx_no_abort_handler = 0; + +static void lx_sigdeliver(int, siginfo_t *, ucontext_t *, size_t, void (*)(), + void (*)(), struct lx_sigaction *); + +/* + * Cache result of process.max-file-descriptor to avoid calling getrctl() + * for each lx_ppoll(). + */ +static rlim_t maxfd = 0; + +/* + * 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 = siginfop->si_utime; + lx_siginfo.lsi_stime = 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 != 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 (oss != 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 != 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 == 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%x!", caller, sp, LX_SIGRT_MAGIC, + lxsdf.lxsdf_magic); + } else { + lx_err_fatal("%s: sp 0x%p, could not read " + "magic", caller, 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); + 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; + + /* 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); + 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. + */ +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). + */ +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 : 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)(); + 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); + } + + if (lxsap->lxsa_handler == SIG_DFL || lxsap->lxsa_handler == SIG_IGN) + 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, + 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]; + /* + * These variables must be "volatile", as they are modified after the + * getcontext() stores the register state: + */ + volatile boolean_t signal_delivered = B_FALSE; + volatile uintptr_t lxfp; + volatile uintptr_t old_tsd_sp; + volatile int newstack; + + /* + * 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)); + + /* + * 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); + 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); + } + } + + /* + * 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 + + if (syscall(SYS_brand, B_JUMP_TO_LINUX, &jump_uc) == -1) { + lx_err_fatal("B_JUMP_TO_LINUX failed: %s", + strerror(errno)); + } + } + + assert(0); + +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 != 0) { + /* + * 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: %s", sig, + strerror(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: " + "%s", sig, strerror(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: " + "%s", sig, strerror(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: %s", + sig, ((sa.sa_handler == SIG_DFL) ? + "SIG_DFL" : "SIG_IGN"), + strerror(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; + + if (getenv("LX_NO_ABORT_HANDLER") != NULL) { + lx_no_abort_handler = 1; + } + + /* + * 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); +} + +/* + * This code strongly resembles lx_poll(), but is here to be able to take + * advantage of the Linux signal helper routines. + */ +long +lx_ppoll(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5) +{ + struct pollfd *lfds, *sfds; + nfds_t nfds = (nfds_t)p2; + timespec_t ts, *tsp = NULL; + int fds_size, i, rval, revents; + lx_sigset_t lxsig, *lxsigp = NULL; + sigset_t sigset, *sp = NULL; + rctlblk_t *rblk; + + lx_debug("\tppoll(0x%p, %d, 0x%p, 0x%p, %d)", p1, p2, p3, p4, p5); + + if (p3 != NULL) { + if (uucopy((void *)p3, &ts, sizeof (ts)) != 0) + return (-errno); + + tsp = &ts; + } + + if (p4 != NULL) { + if (uucopy((void *)p4, &lxsig, sizeof (lxsig)) != 0) + return (-errno); + + lxsigp = &lxsig; + if ((size_t)p5 != sizeof (lx_sigset_t)) + return (-EINVAL); + + if (lxsigp) { + if ((rval = ltos_sigset(lxsigp, &sigset)) != 0) + return (rval); + + sp = &sigset; + } + } + + /* + * Deal with the NULL fds[] case. + */ + if (nfds == 0 || p1 == NULL) { + if ((rval = ppoll(NULL, 0, tsp, sp)) < 0) + return (-errno); + + return (rval); + } + + if (maxfd == 0) { + if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rctlblk_size())) == NULL) + return (-ENOMEM); + + if (getrctl("process.max-file-descriptor", NULL, rblk, + RCTL_FIRST) == -1) + return (-EINVAL); + + maxfd = rctlblk_get_value(rblk); + } + + if (nfds > maxfd) + return (-EINVAL); + + /* + * Note: we are assuming that the Linux and Illumos pollfd + * structures are identical. Copy in the Linux poll structure. + */ + fds_size = sizeof (struct pollfd) * nfds; + lfds = (struct pollfd *)SAFE_ALLOCA(fds_size); + if (lfds == NULL) + return (-ENOMEM); + if (uucopy((void *)p1, lfds, fds_size) != 0) + return (-errno); + + /* + * The poll system call modifies the poll structures passed in + * so we'll need to make an extra copy of them. + */ + sfds = (struct pollfd *)SAFE_ALLOCA(fds_size); + if (sfds == NULL) + return (-ENOMEM); + + /* Convert the Linux events bitmask into the Illumos equivalent. */ + for (i = 0; i < nfds; i++) { + /* + * If the caller is polling for an unsupported event, we + * have to bail out. + */ + if (lfds[i].events & ~LX_POLL_SUPPORTED_EVENTS) { + lx_unsupported("unsupported poll events requested: " + "events=0x%x", lfds[i].events); + return (-ENOTSUP); + } + + sfds[i].fd = lfds[i].fd; + sfds[i].events = lfds[i].events & LX_POLL_COMMON_EVENTS; + if (lfds[i].events & LX_POLLWRNORM) + sfds[i].events |= POLLWRNORM; + if (lfds[i].events & LX_POLLWRBAND) + sfds[i].events |= POLLWRBAND; + if (lfds[i].events & LX_POLLRDHUP) + sfds[i].events |= POLLRDHUP; + sfds[i].revents = 0; + } + + if ((rval = ppoll(sfds, nfds, tsp, sp)) < 0) + return (-errno); + + /* Convert the Illumos revents bitmask into the Linux equivalent */ + for (i = 0; i < nfds; i++) { + revents = sfds[i].revents & LX_POLL_COMMON_EVENTS; + if (sfds[i].revents & POLLWRBAND) + revents |= LX_POLLWRBAND; + if (sfds[i].revents & POLLRDHUP) + revents |= LX_POLLRDHUP; + + /* + * Be careful because on Illumos POLLOUT and POLLWRNORM + * are defined to the same values but on Linux they + * are not. + */ + if (sfds[i].revents & POLLOUT) { + if ((lfds[i].events & LX_POLLOUT) == 0) + revents &= ~LX_POLLOUT; + if (lfds[i].events & LX_POLLWRNORM) + revents |= LX_POLLWRNORM; + } + + lfds[i].revents = revents; + } + + /* Copy out the results */ + if (uucopy(lfds, (void *)p1, fds_size) != 0) + return (-errno); + + return (rval); +} + +/* + * This code stongly resemebles lx_select(), but is here to be able to take + * advantage of the Linux signal helper routines. + */ +long +lx_pselect6(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, + uintptr_t p5, uintptr_t p6) +{ + int nfds = (int)p1; + fd_set *rfdsp = NULL; + fd_set *wfdsp = NULL; + fd_set *efdsp = NULL; + timespec_t ts, *tsp = NULL; + int fd_set_len = howmany(nfds, 8); + int r; + sigset_t sigset, *sp = NULL; + + lx_debug("\tpselect6(%d, 0x%p, 0x%p, 0x%p, 0x%p, 0x%p)", + p1, p2, p3, p4, p4, p6); + + if (nfds > 0) { + if (p2 != NULL) { + rfdsp = SAFE_ALLOCA(fd_set_len); + if (rfdsp == NULL) + return (-ENOMEM); + if (uucopy((void *)p2, rfdsp, fd_set_len) != 0) + return (-errno); + } + if (p3 != NULL) { + wfdsp = SAFE_ALLOCA(fd_set_len); + if (wfdsp == NULL) + return (-ENOMEM); + if (uucopy((void *)p3, wfdsp, fd_set_len) != 0) + return (-errno); + } + if (p4 != NULL) { + efdsp = SAFE_ALLOCA(fd_set_len); + if (efdsp == NULL) + return (-ENOMEM); + if (uucopy((void *)p4, efdsp, fd_set_len) != 0) + return (-errno); + } + } + + if (p5 != NULL) { + if (uucopy((void *)p5, &ts, sizeof (ts)) != 0) + return (-errno); + + tsp = &ts; + } + + if (p6 != NULL) { + /* + * To force the number of arguments to be no more than six, + * Linux bundles both the sigset and the size into a structure + * that becomes the sixth argument. + */ + struct { + lx_sigset_t *addr; + size_t size; + } lx_sigset; + + if (uucopy((void *)p6, &lx_sigset, sizeof (lx_sigset)) != 0) + return (-errno); + + /* + * Yes, that's right: Linux forces a size to be passed only + * so it can check that it's the size of a sigset_t. + */ + if (lx_sigset.size != sizeof (lx_sigset_t)) + return (-EINVAL); + + /* + * This is where we check if the sigset is *really* NULL. + */ + if (lx_sigset.addr) { + if ((r = ltos_sigset(lx_sigset.addr, &sigset)) != 0) + return (r); + + sp = &sigset; + } + } + +#if defined(_LP64) + r = pselect(nfds, rfdsp, wfdsp, efdsp, tsp, sp); +#else + if (nfds >= FD_SETSIZE) + r = pselect_large_fdset(nfds, rfdsp, wfdsp, efdsp, tsp, sp); + else + r = pselect(nfds, rfdsp, wfdsp, efdsp, tsp, sp); +#endif + + if (r < 0) + return (-errno); + + /* + * For pselect6(), we don't honor the strange Linux select() semantics + * with respect to the timestruc parameter because glibc ignores it + * anyway -- just copy out the fd pointers and return. + */ + if ((rfdsp != NULL) && (uucopy(rfdsp, (void *)p2, fd_set_len) != 0)) + return (-errno); + if ((wfdsp != NULL) && (uucopy(wfdsp, (void *)p3, fd_set_len) != 0)) + return (-errno); + if ((efdsp != NULL) && (uucopy(efdsp, (void *)p4, fd_set_len) != 0)) + return (-errno); + + return (r); +} + +/* + * 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); +} diff --git a/usr/src/lib/brand/lx/lx_brand/common/socket.c b/usr/src/lib/brand/lx/lx_brand/common/socket.c new file mode 100644 index 0000000000..d5e5d90199 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/socket.c @@ -0,0 +1,2842 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <libintl.h> +#include <strings.h> +#include <alloca.h> +#include <ucred.h> +#include <limits.h> + +#include <sys/param.h> +#include <sys/brand.h> +#include <sys/syscall.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/un.h> +#include <netinet/tcp.h> +#include <netinet/igmp.h> +#include <netinet/icmp6.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/lx_debug.h> +#include <sys/lx_syscall.h> +#include <sys/lx_socket.h> +#include <sys/lx_brand.h> +#include <sys/lx_misc.h> +#include <netpacket/packet.h> + +/* + * This string is used to prefix all abstract namespace Unix sockets, ie all + * abstract namespace sockets are converted to regular sockets in the /tmp + * directory with .ABSK_ prefixed to their names. + */ +#define ABST_PRFX "/tmp/.ABSK_" +#define ABST_PRFX_LEN 11 + +#define LX_DEV_LOG "/dev/log" +#define LX_DEV_LOG_REDIRECT "/var/run/.dev_log_redirect" +#define LX_DEV_LOG_REDIRECT_LEN 18 + +typedef enum { + lxa_none, + lxa_abstract, + lxa_devlog +} lx_addr_type_t; + +#ifdef __i386 + +static int lx_socket32(ulong_t *); +static int lx_bind32(ulong_t *); +static int lx_connect32(ulong_t *); +static int lx_listen32(ulong_t *); +static int lx_accept32(ulong_t *); +static int lx_getsockname32(ulong_t *); +static int lx_getpeername32(ulong_t *); +static int lx_socketpair32(ulong_t *); +static int lx_send(ulong_t *); +static int lx_recv(ulong_t *); +static int lx_sendto32(ulong_t *); +static int lx_recvfrom32(ulong_t *); +static int lx_shutdown32(ulong_t *); +static int lx_setsockopt32(ulong_t *); +static int lx_getsockopt32(ulong_t *); +static int lx_sendmsg32(ulong_t *); +static int lx_recvmsg32(ulong_t *); +static int lx_accept4_32(ulong_t *); +static int lx_recvmmsg32(ulong_t *); +static int lx_sendmmsg32(ulong_t *); + +typedef int (*sockfn_t)(ulong_t *); + +static struct { + sockfn_t s_fn; /* Function implementing the subcommand */ + int s_nargs; /* Number of arguments the function takes */ +} sockfns[] = { + lx_socket32, 3, + lx_bind32, 3, + lx_connect32, 3, + lx_listen32, 2, + lx_accept32, 3, + lx_getsockname32, 3, + lx_getpeername32, 3, + lx_socketpair32, 4, + lx_send, 4, + lx_recv, 4, + lx_sendto32, 6, + lx_recvfrom32, 6, + lx_shutdown32, 2, + lx_setsockopt32, 5, + lx_getsockopt32, 5, + lx_sendmsg32, 3, + lx_recvmsg32, 3, + lx_accept4_32, 4, + lx_recvmmsg32, 5, + lx_sendmmsg32, 4 +}; +#endif /* __i386 */ + +/* + * What follows are a series of tables we use to translate Linux constants + * into equivalent Illumos constants and back again. I wish this were + * cleaner, more programmatic, and generally nicer. Sadly, life is messy, + * and Unix networking even more so. + */ +static const int ltos_family[LX_AF_MAX + 1] = { + AF_UNSPEC, AF_UNIX, AF_INET, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_INET6, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_LX_NETLINK, + AF_PACKET, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED +}; + +#define LX_AF_INET6 10 +#define LX_AF_NETLINK 16 +#define LX_AF_PACKET 17 + +static const int stol_family[LX_AF_MAX + 1] = { + AF_UNSPEC, AF_UNIX, AF_INET, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, LX_AF_INET6, AF_NOTSUPPORTED, AF_NOTSUPPORTED, + AF_NOTSUPPORTED, AF_NOTSUPPORTED, AF_NOTSUPPORTED, LX_AF_PACKET, + LX_AF_NETLINK +}; + +#define LTOS_FAMILY(d) ((d) <= LX_AF_MAX ? ltos_family[(d)] : AF_INVAL) +#define STOL_FAMILY(d) ((d) <= LX_AF_MAX ? stol_family[(d)] : AF_INVAL) + +static const int ltos_socktype[LX_SOCK_PACKET + 1] = { + SOCK_NOTSUPPORTED, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, + SOCK_RDM, SOCK_SEQPACKET, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED, + SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED +}; + +#define LTOS_SOCKTYPE(t) \ + ((t) <= LX_SOCK_PACKET ? ltos_socktype[(t)] : SOCK_INVAL) + +static const int stol_socktype[SOCK_SEQPACKET + 1] = { + SOCK_NOTSUPPORTED, LX_SOCK_DGRAM, LX_SOCK_STREAM, SOCK_NOTSUPPORTED, + LX_SOCK_RAW, LX_SOCK_RDM, LX_SOCK_SEQPACKET +}; + +/* + * Linux socket option type definitions + * + * The protocol `levels` are well defined (see in.h) The option values are + * not so well defined. Linux often uses different values vs. Illumos + * although they mean the same thing. For example, IP_TOS in Linux is + * defined as value 1 but in Illumos it is defined as value 3. This table + * maps all the Protocol levels to their options and maps them between + * Linux and Illumos and vice versa. Hence the reason for the complexity. + * + * For a certain subset of sockopts, Linux will implicitly truncate optval + * input, so long as optlen meets a minimum size. Because illumos is strict + * about optlen, we must cap optlen for those options. + */ + +typedef struct lx_sockopt_map { + const int lsm_opt; /* Illumos-native equivalent */ + const int lsm_lcap; /* Cap optlen to this size. (Ignored if 0) */ +} lx_sockopt_map_t; + +typedef struct lx_proto_opts { + const lx_sockopt_map_t *proto; /* Linux to Illumos mapping table */ + int maxentries; /* max entries in this table */ +} lx_proto_opts_t; + +#define OPTNOTSUP -1 /* we don't support it */ + + +/* + * Linux Illumos + * ----- ------- + * IP_TOS 1 IP_TOS 3 + * IP_TTL 2 IP_TTL 4 + * IP_HDRINCL 3 IP_HDRINCL 2 + * IP_OPTIONS 4 IP_OPTIONS 1 + * IP_ROUTER_ALERT 5 + * IP_RECVOPTS 6 IP_RECVOPTS 5 + * IP_RETOPTS 7 IP_RETOPTS 8 + * IP_PKTINFO 8 + * IP_PKTOPTIONS 9 + * IP_MTU_DISCOVER 10 emulated for traceroute + * IP_RECVERR 11 emulated for traceroute + * IP_RECVTTL 12 IP_RECVTTL 11 + * IP_RECVTOS 13 + * IP_MTU 14 + * IP_FREEBIND 15 + * IP_IPSEC_POLICY 16 + * IP_XFRM_POLICY 17 + * IP_PASSSEC 18 + * IP_TRANSPARENT 19 + * IP_ORIGDSTADDR 20 + * IP_MINTTL 21 + * IP_NODEFRAG 22 + * + * apparent gap + * + * IP_MULTICAST_IF 32 IP_MULTICAST_IF 16 + * IP_MULTICAST_TTL 33 IP_MULTICAST_TTL 17 + * IP_MULTICAST_LOOP 34 IP_MULTICAST_LOOP 18 + * IP_ADD_MEMBERSHIP 35 IP_ADD_MEMBERSHIP 19 + * IP_DROP_MEMBERSHIP 36 IP_DROP_MEMBERSHIP 20 + * IP_UNBLOCK_SOURCE 37 IP_UNBLOCK_SOURCE 22 + * IP_BLOCK_SOURCE 38 IP_BLOCK_SOURCE 21 + * IP_ADD_SOURCE_MEMBERSHIP 39 IP_ADD_SOURCE_MEMBERSHIP 23 + * IP_DROP_SOURCE_MEMBERSHIP 40 IP_DROP_SOURCE_MEMBERSHIP 24 + * IP_MSFILTER 41 + * MCAST_JOIN_GROUP 42 -> MCAST_JOIN_GROUP + * MCAST_BLOCK_SOURCE 43 -> MCAST_BLOCK_SOURCE + * MCAST_UNBLOCK_SOURCE 44 -> MCAST_UNBLOCK_SOURCE + * MCAST_LEAVE_GROUP 45 -> MCAST_LEAVE_GROUP + * MCAST_JOIN_SOURCE_GROUP 46 -> MCAST_JOIN_SOURCE_GROUP + * MCAST_LEAVE_SOURCE_GROUP 47 -> MCAST_LEAVE_SOURCE_GROUP + * MCAST_MSFILTER 48 + * IP_MULTICAST_ALL 49 + * IP_UNICAST_IF 50 + * + * The Illumos options preceeded by '->' can be added but we might also need + * emulation to convert the ip_mreq_source struct. + */ +static const lx_sockopt_map_t ltos_ip_sockopts[LX_IP_UNICAST_IF + 1] = { + { OPTNOTSUP, 0 }, + { IP_TOS, sizeof (int) }, + { IP_TTL, sizeof (int) }, + { IP_HDRINCL, sizeof (int) }, + { IP_OPTIONS, 0 }, + { OPTNOTSUP, 0 }, + { IP_RECVOPTS, sizeof (int) }, + { IP_RETOPTS, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IP_RECVTTL, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IP_MULTICAST_IF, sizeof (int) }, + { IP_MULTICAST_TTL, sizeof (int) }, + { IP_MULTICAST_LOOP, sizeof (int) }, + { IP_ADD_MEMBERSHIP, 0 }, + { IP_DROP_MEMBERSHIP, 0 }, + { IP_UNBLOCK_SOURCE, 0 }, + { IP_BLOCK_SOURCE, 0 }, + { IP_ADD_SOURCE_MEMBERSHIP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 } +}; + +/* + * Linux Illumos + * ----- ------- + * + * IPV6_ADDRFORM 1 + * IPV6_2292PKTINFO 2 + * IPV6_2292HOPOPTS 3 + * IPV6_2292DSTOPTS 4 + * IPV6_2292RTHDR 5 + * IPV6_2292PKTOPTIONS 6 + * IPV6_CHECKSUM 7 IPV6_CHECKSUM 0x18 + * IPV6_2292HOPLIMIT 8 + * IPV6_NEXTHOP 9 + * IPV6_AUTHHDR 10 + * IPV6_UNICAST_HOPS 16 IPV6_UNICAST_HOPS 0x5 + * IPV6_MULTICAST_IF 17 IPV6_MULTICAST_IF 0x6 + * IPV6_MULTICAST_HOPS 18 IPV6_MULTICAST_HOPS 0x7 + * IPV6_MULTICAST_LOOP 19 IPV6_MULTICAST_LOOP 0x8 + * IPV6_JOIN_GROUP 20 + * IPV6_LEAVE_GROUP 21 + * IPV6_ROUTER_ALERT 22 + * IPV6_MTU_DISCOVER 23 + * IPV6_MTU 24 (discarded) + * IPV6_RECVERR 25 + * IPV6_V6ONLY 26 IPV6_V6ONLY 0x27 + * IPV6_JOIN_ANYCAST 27 + * IPV6_LEAVE_ANYCAST 28 + * IPV6_IPSEC_POLICY 34 + * IPV6_XFRM_POLICY 35 + * + * IPV6_RECVPKTINFO 49 IPV6_RECVPKTINFO 0x12 + * IPV6_PKTINFO 50 IPV6_PKTINFO 0xb + * IPV6_RECVHOPLIMIT 51 IPV6_RECVHOPLIMIT 0x13 + * IPV6_HOPLIMIT 52 IPV6_HOPLIMIT 0xc + * IPV6_RECVHOPOPTS 53 + * IPV6_HOPOPTS 54 + * IPV6_RTHDRDSTOPTS 55 + * IPV6_RECVRTHDR 56 + * IPV6_RTHDR 57 + * IPV6_RECVDSTOPTS 58 + * IPV6_DSTOPTS 59 + * IPV6_RECVTCLASS 66 + * IPV6_TCLASS 67 IPV6_TCLASS 0x26 + */ + + +static const lx_sockopt_map_t ltos_ipv6_sockopts[LX_IPV6_TCLASS + 1] = { + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IPV6_CHECKSUM, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IPV6_UNICAST_HOPS, sizeof (int) }, + { IPV6_MULTICAST_IF, sizeof (int) }, + { IPV6_MULTICAST_HOPS, sizeof (int) }, + { IPV6_MULTICAST_LOOP, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IPV6_V6ONLY, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IPV6_RECVPKTINFO, sizeof (int) }, + { IPV6_PKTINFO, 0 }, + { IPV6_RECVHOPLIMIT, sizeof (int) }, + { IPV6_HOPLIMIT, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IPV6_TCLASS, sizeof (int) } +}; + +/* + * Linux Illumos + * ----- ------- + * + * ICMP6_FILTER 1 ICMP6_FILTER 1 + */ + +static const lx_sockopt_map_t ltos_icmpv6_sockopts[LX_ICMP6_FILTER + 1] = { + { OPTNOTSUP, 0 }, + { ICMP6_FILTER, 0 } +}; + +/* + * + * TCP socket option mapping: + * + * Linux Illumos + * ----- ------- + * TCP_NODELAY 1 TCP_NODELAY 1 + * TCP_MAXSEG 2 TCP_MAXSEG 2 + * TCP_CORK 3 TCP_CORK 24 + * TCP_KEEPIDLE 4 TCP_KEEPIDLE 34 + * TCP_KEEPINTVL 5 TCP_KEEPINTVL 36 + * TCP_KEEPCNT 6 TCP_KEEPCNT 35 + * TCP_SYNCNT 7 + * TCP_LINGER2 8 TCP_LINGER2 28 + * TCP_DEFER_ACCEPT 9 + * TCP_WINDOW_CLAMP 10 + * TCP_INFO 11 + * TCP_QUICKACK 12 + * TCP_CONGESTION 13 + * TCP_MD5SIG 14 + * TCP_THIN_LINEAR_TIMEOUTS 16 + * TCP_THIN_DUPACK 17 + * TCP_USER_TIMEOUT 18 + * TCP_REPAIR 19 + * TCP_REPAIR_QUEUE 20 + * TCP_QUEUE_SEQ 21 + * TCP_REPAIR_OPTIONS 22 + * TCP_FASTOPEN 23 + * TCP_TIMESTAMP 24 + * TCP_NOTSENT_LOWAT 25 + */ + +static const lx_sockopt_map_t ltos_tcp_sockopts[LX_TCP_NOTSENT_LOWAT + 1] = { + { OPTNOTSUP, 0 }, + { TCP_NODELAY, sizeof (int) }, + { TCP_MAXSEG, sizeof (int) }, + { TCP_CORK, sizeof (int) }, + { TCP_KEEPIDLE, sizeof (int) }, + { TCP_KEEPINTVL, sizeof (int) }, + { TCP_KEEPCNT, sizeof (int) }, + { OPTNOTSUP, 0 }, + { TCP_LINGER2, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 } +}; + +static const lx_sockopt_map_t ltos_igmp_sockopts[IGMP_MTRACE + 1] = { + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IGMP_MINLEN, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IGMP_MEMBERSHIP_QUERY, 0 }, + { IGMP_V1_MEMBERSHIP_REPORT, 0 }, + { IGMP_DVMRP, 0 }, + { IGMP_PIM, 0 }, + { OPTNOTSUP, 0 }, + { IGMP_V2_MEMBERSHIP_REPORT, 0 }, + { IGMP_V2_LEAVE_GROUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { IGMP_MTRACE_RESP, 0 }, + { IGMP_MTRACE, 0 } +}; + +/* + * Socket option mapping: + * + * Linux Illumos + * ----- ------- + * SO_DEBUG 1 SO_DEBUG 0x0001 + * SO_REUSEADDR 2 SO_REUSEADDR 0x0004 + * SO_TYPE 3 SO_TYPE 0x1008 + * SO_ERROR 4 SO_ERROR 0x1007 + * SO_DONTROUTE 5 SO_DONTROUTE 0x0010 + * SO_BROADCAST 6 SO_BROADCAST 0x0020 + * SO_SNDBUF 7 SO_SNDBUF 0x1001 + * SO_RCVBUF 8 SO_RCVBUF 0x1002 + * SO_KEEPALIVE 9 SO_KEEPALIVE 0x0008 + * SO_OOBINLINE 10 SO_OOBINLINE 0x0100 + * SO_NO_CHECK 11 + * SO_PRIORITY 12 + * SO_LINGER 13 SO_LINGER 0x0080 + * SO_BSDCOMPAT 14 ignored by linux, emulation returns 0 + * SO_REUSEPORT 15 + * SO_PASSCRED 16 SO_RECVUCRED 0x0400 + * SO_PEERCRED 17 emulated with getpeerucred + * SO_RCVLOWAT 18 SO_RCVLOWAT 0x1004 + * SO_SNDLOWAT 19 SO_SNDLOWAT 0x1003 + * SO_RCVTIMEO 20 SO_RCVTIMEO 0x1006 + * SO_SNDTIMEO 21 SO_SNDTIMEO 0x1005 + * SO_SECURITY_AUTHENTICATION 22 + * SO_SECURITY_ENCRYPTION_TRANSPORT 23 + * SO_SECURITY_ENCRYPTION_NETWORK 24 + * SO_BINDTODEVICE 25 + * SO_ATTACH_FILTER 26 SO_ATTACH_FILTER 0x40000001 + * SO_DETACH_FILTER 27 SO_DETACH_FILTER 0x40000002 + * SO_PEERNAME 28 + * SO_TIMESTAMP 29 SO_TIMESTAMP 0x1013 + * SO_ACCEPTCONN 30 SO_ACCEPTCONN 0x0002 + * SO_PEERSEC 31 + * SO_SNDBUFFORCE 32 SO_SNDBUF (FORCE is a lie) + * SO_RCVBUFFORCE 33 SO_RCVBUF (FORCE is a lie) + * SO_PASSSEC 34 + * SO_TIMESTAMPNS 35 + * SO_MARK 36 + * SO_TIMESTAMPING 37 + * SO_PROTOCOL 38 SO_PROTOTYPE 0x1009 + * SO_DOMAIN 39 SO_DOMAIN 0x100c + * SO_RXQ_OVFL 40 + * SO_WIFI_STATUS 41 + * SO_PEEK_OFF 42 + * SO_NOFCS 43 + * SO_LOCK_FILTER 44 + * SO_SELECT_ERR_QUEUE 45 + * SO_BUSY_POLL 46 + * SO_MAX_PACING_RATE 47 + * SO_BPF_EXTENSIONS 48 + */ +static const lx_sockopt_map_t ltos_socket_sockopts[LX_SO_BPF_EXTENSIONS + 1] = { + { OPTNOTSUP, 0 }, + { SO_DEBUG, sizeof (int) }, + { SO_REUSEADDR, sizeof (int) }, + { SO_TYPE, 0 }, + { SO_ERROR, 0 }, + { SO_DONTROUTE, sizeof (int) }, + { SO_BROADCAST, sizeof (int) }, + { SO_SNDBUF, sizeof (int) }, + { SO_RCVBUF, sizeof (int) }, + { SO_KEEPALIVE, sizeof (int) }, + { SO_OOBINLINE, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { SO_LINGER, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { SO_RECVUCRED, sizeof (int) }, + { OPTNOTSUP, 0 }, + { SO_RCVLOWAT, sizeof (int) }, + { SO_SNDLOWAT, sizeof (int) }, + { SO_RCVTIMEO, 0 }, + { SO_SNDTIMEO, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { SO_ATTACH_FILTER, 0 }, + { SO_DETACH_FILTER, 0 }, + { OPTNOTSUP, 0 }, + { SO_TIMESTAMP, sizeof (int) }, + { SO_ACCEPTCONN, 0 }, + { OPTNOTSUP, 0 }, + { SO_SNDBUF, sizeof (int) }, + { SO_RCVBUF, sizeof (int) }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { SO_PROTOTYPE, 0 }, + { SO_DOMAIN, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 } +}; + +/* + * See the Linux raw.7 man page for description of the socket options. + * In Linux ICMP_FILTER is defined as 1 in include/uapi/linux/icmp.h + */ +static const lx_sockopt_map_t ltos_raw_sockopts[LX_IPV6_CHECKSUM + 1] = { + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 } +}; + +/* + * PF_PACKET sockopts + * Linux Illumos + * ----- ------- + * PACKET_ADD_MEMBERSHIP 1 PACKET_ADD_MEMBERSHIP 0x2 + * PACKET_DROP_MEMBERSHIP 2 PACKET_DROP_MEMBERSHIP 0x3 + * PACKET_RECV_OUTPUT 3 + * PACKET_RX_RING 5 + * PACKET_STATISTICS 6 PACKET_STATISTICS 0x5 + */ + +static const lx_sockopt_map_t ltos_packet_sockopts[LX_PACKET_STATISTICS + 1] = { + { OPTNOTSUP, 0 }, + { PACKET_ADD_MEMBERSHIP, 0 }, + { PACKET_DROP_MEMBERSHIP, 0 }, + { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, { OPTNOTSUP, 0 }, + { PACKET_STATISTICS, 0 } +}; + +#define PROTO_SOCKOPTS(opts) \ + { (opts), sizeof ((opts)) / sizeof ((opts)[0]) } + +/* + * [gs]etsockopt options mapping tables + */ +static lx_proto_opts_t ip_sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts); +static lx_proto_opts_t ipv6_sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts); +static lx_proto_opts_t icmpv6_sockopts_tbl = + PROTO_SOCKOPTS(ltos_icmpv6_sockopts); +static lx_proto_opts_t socket_sockopts_tbl = + PROTO_SOCKOPTS(ltos_socket_sockopts); +static lx_proto_opts_t igmp_sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts); +static lx_proto_opts_t tcp_sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts); +static lx_proto_opts_t raw_sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts); +static lx_proto_opts_t packet_sockopts_tbl = + PROTO_SOCKOPTS(ltos_packet_sockopts); +/* lx_netlink does straight passthrough, so fake a table for it */ +static lx_proto_opts_t netlink_sockopts_tbl = { + NULL, + LX_SOL_NETLINK_MAX_ENTRY +}; + + +/* Needed for SO_ATTACH_FILTER */ +struct lx_bpf_program { + unsigned short bf_len; + caddr_t bf_insns; +}; + +/* + * Lifted from socket.h, since these definitions are contained within + * _KERNEL guards. + */ +#define _CMSG_HDR_ALIGNMENT 4 +#define _CMSG_HDR_ALIGN(x) (((uintptr_t)(x) + _CMSG_HDR_ALIGNMENT - 1) & \ + ~(_CMSG_HDR_ALIGNMENT - 1)) +#define _CMSG_DATA_ALIGN(x) \ + (((uintptr_t)(x) + sizeof (int) - 1) & ~(sizeof (int) - 1)) + +#define CMSG_FIRSTHDR(m) \ + (((m)->msg_controllen < sizeof (struct cmsghdr)) ? \ + (struct cmsghdr *)0 : (struct cmsghdr *)((m)->msg_control)) + +#define CMSG_NXTHDR(m, c) \ + (((c) == 0) ? CMSG_FIRSTHDR(m) : \ + ((((uintptr_t)_CMSG_HDR_ALIGN((char *)(c) + \ + ((struct cmsghdr *)(c))->cmsg_len) + sizeof (struct cmsghdr)) > \ + (((uintptr_t)((struct lx_msghdr *)(m))->msg_control) + \ + ((uintptr_t)((struct lx_msghdr *)(m))->msg_controllen))) ? \ + ((struct cmsghdr *)0) : \ + ((struct cmsghdr *)_CMSG_HDR_ALIGN((char *)(c) + \ + ((struct cmsghdr *)(c))->cmsg_len)))) + +#define CMSG_LEN(l) \ + ((unsigned int)_CMSG_DATA_ALIGN(sizeof (struct cmsghdr)) + (l)) + +#define CMSG_DATA(c) \ + ((unsigned char *)_CMSG_DATA_ALIGN((struct cmsghdr *)(c) + 1)) + +#define LX_TO_SOL 1 +#define SOL_TO_LX 2 + +#define LX_NETLINK_KOBJECT_UEVENT 15 +#define LX_NETLINK_ROUTE 0 + +typedef struct { + sa_family_t nl_family; + unsigned short nl_pad; + uint32_t nl_pid; + uint32_t nl_groups; +} lx_sockaddr_nl_t; + +typedef struct { + sa_family_t sin6_family; + in_port_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; /* Depends on scope of sin6_addr */ + /* one 32-bit field shorter than illumos */ +} lx_sockaddr_in6_t; + +#if defined(_LP64) +/* + * For 32-bit code the Illumos and Linux cmsghdr structure definition is the + * same, but for 64-bit Linux code the cmsg_len value is a long instead of an + * int. As a result, we need to go through a bunch of work to transform the + * csmgs back and forth. + */ +typedef struct { + long cmsg_len; + int cmsg_level; + int cmsg_type; +} lx_cmsghdr64_t; + +/* + * When converting from Illumos to Linux we don't know in advance how many + * control msgs we recv, but we do know that the Linux header is 4 bytes + * bigger, plus any additional alignment bytes. We'll take a guess and assume + * there is not 64 msgs (1 is common) and alloc an extra 256 bytes. + */ +#define LX_CMSG_EXTRA 256 + +#define LX_CMSG_HDR_ALIGN(x) \ + (((uintptr_t)(x) + sizeof (long) - 1) & ~(sizeof (long) - 1)) + +#define LX_CMSG_DATA_ALIGN(x) \ + (((uintptr_t)(x) + sizeof (int) - 1) & ~(sizeof (int) - 1)) + +#define LX_CMSG_DATA(c) \ + ((unsigned char *)LX_CMSG_DATA_ALIGN((lx_cmsghdr64_t *)(c) + 1)) + +#define LX_CMSG_FIRSTHDR(m) \ + (((m)->msg_controllen < sizeof (lx_cmsghdr64_t)) ? \ + (lx_cmsghdr64_t *)NULL : (lx_cmsghdr64_t *)((m)->msg_control)) + +#define LX_CMSG_LEN(l) (LX_CMSG_HDR_ALIGN(sizeof (lx_cmsghdr64_t)) + (l)) + +#define LX_CMSG_NXTHDR(m, c) \ + (((c) == 0) ? LX_CMSG_FIRSTHDR(m) : \ + ((((uintptr_t)LX_CMSG_HDR_ALIGN((char *)(c) + \ + ((lx_cmsghdr64_t *)(c))->cmsg_len) + sizeof (lx_cmsghdr64_t)) > \ + (((uintptr_t)((struct lx_msghdr *)(m))->msg_control) + \ + ((uintptr_t)((struct lx_msghdr *)(m))->msg_controllen))) ? \ + ((lx_cmsghdr64_t *)0) : \ + ((lx_cmsghdr64_t *)LX_CMSG_HDR_ALIGN((char *)(c) + \ + ((lx_cmsghdr64_t *)(c))->cmsg_len)))) + + +static void +ltos_xform_cmsgs(struct lx_msghdr *msg, struct cmsghdr *ntv_cmsg) +{ + lx_cmsghdr64_t *lcmsg, *last; + struct cmsghdr *cmsg, *lp; + int nlen = 0; + + cmsg = ntv_cmsg; + lcmsg = LX_CMSG_FIRSTHDR(msg); + while (lcmsg != NULL) { + cmsg->cmsg_len = + CMSG_LEN(lcmsg->cmsg_len - sizeof (lx_cmsghdr64_t)); + cmsg->cmsg_level = lcmsg->cmsg_level; + cmsg->cmsg_type = lcmsg->cmsg_type; + + bcopy((void *)LX_CMSG_DATA(lcmsg), (void *)CMSG_DATA(cmsg), + lcmsg->cmsg_len - sizeof (lx_cmsghdr64_t)); + + last = lcmsg; + lcmsg = LX_CMSG_NXTHDR(msg, last); + + lp = cmsg; + cmsg = (struct cmsghdr *)_CMSG_HDR_ALIGN((char *)cmsg + + cmsg->cmsg_len); + + nlen += (int)((uint64_t)cmsg - (uint64_t)lp); + } + + msg->msg_control = ntv_cmsg; + msg->msg_controllen = nlen; +} + +static int +stol_xform_cmsgs(struct lx_msghdr *msg, lx_cmsghdr64_t *lx_cmsg) +{ + struct lx_msghdr tmsg; + lx_cmsghdr64_t *lcmsg, *last; + struct cmsghdr *cmsg, *lp; + int nlen = 0; + int err = 0; + + /* + * Create a temporary "struct lx_msghdr" so that we can use the + * LX_CMSG_*HDR() iteration macros. + */ + tmsg = *msg; + tmsg.msg_control = lx_cmsg; + tmsg.msg_controllen = msg->msg_controllen + LX_CMSG_EXTRA; + + lcmsg = LX_CMSG_FIRSTHDR(&tmsg); + cmsg = CMSG_FIRSTHDR(msg); + while (cmsg != NULL && err == 0) { + if (lcmsg == NULL) { + err = ENOTSUP; + break; + } + + lcmsg->cmsg_len = + LX_CMSG_LEN(cmsg->cmsg_len - sizeof (struct cmsghdr)); + lcmsg->cmsg_level = cmsg->cmsg_level; + lcmsg->cmsg_type = cmsg->cmsg_type; + + bcopy((void *)CMSG_DATA(cmsg), (void *)LX_CMSG_DATA(lcmsg), + cmsg->cmsg_len - sizeof (struct cmsghdr)); + + lp = cmsg; + cmsg = CMSG_NXTHDR(msg, lp); + + last = lcmsg; + lcmsg = LX_CMSG_NXTHDR(&tmsg, last); + + nlen += (int)((uint64_t)lcmsg - (uint64_t)last); + + if (nlen > (msg->msg_controllen + LX_CMSG_EXTRA)) { + err = ENOTSUP; + } + } + + if (err) { + lx_unsupported("stol_xform_cmsgs exceeded the allocation " + "%d %d\n", nlen, (msg->msg_controllen + LX_CMSG_EXTRA)); + } else { + msg->msg_control = lx_cmsg; + msg->msg_controllen = nlen; + } + return (err); +} +#endif + +static int +convert_cmsgs(int direction, struct lx_msghdr *msg, void *new_cmsg, + char *caller) +{ + struct cmsghdr *cmsg, *last; + int err = 0; + int level = 0; + int type = 0; + +#if defined(_LP64) + if (direction == LX_TO_SOL) { + ltos_xform_cmsgs(msg, (struct cmsghdr *)new_cmsg); + } +#endif + + cmsg = CMSG_FIRSTHDR(msg); + while (cmsg != NULL && err == 0) { + level = cmsg->cmsg_level; + type = cmsg->cmsg_type; + + if (direction == LX_TO_SOL) { + if (cmsg->cmsg_level == LX_SOL_SOCKET) { + cmsg->cmsg_level = SOL_SOCKET; + if (cmsg->cmsg_type == LX_SCM_RIGHTS) + cmsg->cmsg_type = SCM_RIGHTS; + else if (cmsg->cmsg_type == LX_SCM_CRED) + cmsg->cmsg_type = SCM_UCRED; + else if (cmsg->cmsg_type == LX_SCM_TIMESTAMP) + cmsg->cmsg_type = SCM_TIMESTAMP; + else + err = ENOTSUP; + } else if (cmsg->cmsg_level == LX_IPPROTO_IPV6) { + if (cmsg->cmsg_type == LX_IPV6_PKTINFO) { + cmsg->cmsg_type = IPV6_PKTINFO; + } + } else { + err = ENOTSUP; + } + } else { + if (cmsg->cmsg_level == SOL_SOCKET) { + cmsg->cmsg_level = LX_SOL_SOCKET; + if (cmsg->cmsg_type == SCM_RIGHTS) + cmsg->cmsg_type = LX_SCM_RIGHTS; + else if (cmsg->cmsg_type == SCM_UCRED) + cmsg->cmsg_type = LX_SCM_CRED; + else if (cmsg->cmsg_type == SCM_TIMESTAMP) + cmsg->cmsg_type = LX_SCM_TIMESTAMP; + else + err = ENOTSUP; + } else if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_PKTINFO) { + cmsg->cmsg_type = LX_IPV6_PKTINFO; + } + } else { + err = ENOTSUP; + } + } + + last = cmsg; + cmsg = CMSG_NXTHDR(msg, last); + } + if (err) + lx_unsupported("Unsupported socket control message %d " + "(%d) in %s\n.", type, level, caller); + +#if defined(_LP64) + if (direction == SOL_TO_LX && err == 0) { + err = stol_xform_cmsgs(msg, (lx_cmsghdr64_t *)new_cmsg); + } +#endif + + return (err); +} + +/* + * We may need a different size socket address vs. the one passed in. + */ +static int +calc_addr_size(struct sockaddr *a, int nlen, lx_addr_type_t *type) +{ + struct sockaddr name; + sa_family_t family; + size_t fsize = sizeof (name.sa_family); + + if (uucopy(a, &name, sizeof (struct sockaddr)) != 0) + return (-errno); + family = LTOS_FAMILY(name.sa_family); + + if (family != AF_UNIX) { + *type = lxa_none; + + if (family == AF_INET6) + return (sizeof (struct sockaddr_in6)); + else if (nlen < sizeof (struct sockaddr)) + return (sizeof (struct sockaddr)); + else + return (nlen); + } + + /* + * Handle Linux abstract sockets, which are Unix sockets whose path + * begins with a NULL character. + */ + if (name.sa_data[0] == '\0') { + *type = lxa_abstract; + return (nlen + ABST_PRFX_LEN); + } + + /* + * For /dev/log, we need to create the Unix domain socket away from + * the (unwritable) /dev. + */ + if (strncmp(name.sa_data, LX_DEV_LOG, nlen - fsize) == 0) { + *type = lxa_devlog; + return (nlen + LX_DEV_LOG_REDIRECT_LEN); + } + + *type = lxa_none; + return (nlen); +} + +static int +convert_pkt_proto(int protocol) +{ + switch (ntohs(protocol)) { + case LX_ETH_P_802_2: + return (ETH_P_802_2); + case LX_ETH_P_IP: + return (ETH_P_IP); + case LX_ETH_P_ARP: + return (ETH_P_ARP); + case LX_ETH_P_IPV6: + return (ETH_P_IPV6); + case LX_ETH_P_ALL: + case LX_ETH_P_802_3: + return (ETH_P_ALL); + default: + return (-1); + } +} + +/* + * If inaddr is an abstract namespace Unix socket, this function expects addr + * to have enough memory to hold the expanded socket name, ie it must be of + * size *len + ABST_PRFX_LEN. If inaddr is a netlink socket then we expect + * addr to have enough memory to hold an Unix socket address. + */ +static int +ltos_sockaddr(struct sockaddr *addr, socklen_t *len, + struct sockaddr *inaddr, socklen_t inlen, lx_addr_type_t type) +{ + sa_family_t family; + struct sockaddr_ll *sll; + int proto; + int size; + int i, orig_len; + + /* + * Note that if the buffer at inaddr is ever smaller than inlen bytes, + * we may erroneously return EFAULT rather than a possible EINVAL + * as the copy comes before the various checks as to whether inlen + * is of the proper length for the socket type. + * + * This isn't an issue at present because all callers to this routine + * do meet that constraint. + */ + if ((int)inlen < 0) + return (-EINVAL); + if (uucopy(inaddr, addr, inlen) != 0) + return (-errno); + + family = LTOS_FAMILY(addr->sa_family); + + switch (family) { + case (sa_family_t)AF_NOTSUPPORTED: + return (-EPROTONOSUPPORT); + case (sa_family_t)AF_INVAL: + return (-EAFNOSUPPORT); + case AF_INET: + size = sizeof (struct sockaddr); + + if (inlen < size) + return (-EINVAL); + + *len = size; + break; + + case AF_INET6: + /* + * The illumos sockaddr_in6 has one more 32-bit field + * than the Linux version. We assume the caller has + * zeroed the sockaddr we're copying into. + */ + if (inlen != sizeof (lx_sockaddr_in6_t)) + return (-EINVAL); + + *len = sizeof (struct sockaddr_in6); + break; + + case AF_UNIX: + if (inlen > sizeof (struct sockaddr_un)) + return (-EINVAL); + + *len = inlen; + + /* + * In order to support /dev/log -- a Unix domain socket + * used for logging that has had its path hard-coded + * far and wide -- we need to relocate the socket + * into a writable filesystem. This also necessitates + * some cleanup in bind(); see lx_bind() for details. + */ + if (type == lxa_devlog) { + *len = inlen + LX_DEV_LOG_REDIRECT_LEN; + strcpy(addr->sa_data, LX_DEV_LOG_REDIRECT); + break; + } + + /* + * Linux supports abstract Unix sockets, which are + * simply sockets that do not exist on the file system. + * These sockets are denoted by beginning the path with + * a NULL character. To support these, we strip out the + * leading NULL character and change the path to point + * to a real place in /tmp directory, by prepending + * ABST_PRFX and replacing all illegal characters with + * '_'. + */ + if (type == lxa_abstract) { + /* + * inlen is the entire size of the sockaddr_un + * data structure, including the sun_family, so + * we need to subtract this out. We subtract + * 1 since we want to overwrite the leadin NULL + * character, and thus do not include it in the + * length. + */ + orig_len = inlen - sizeof (addr->sa_family) - 1; + + /* + * Since abstract paths can contain illegal + * filename characters, we simply replace these + * with '_' + */ + for (i = 1; i < orig_len + 1; i++) { + if (addr->sa_data[i] == '\0' || + addr->sa_data[i] == '/') + addr->sa_data[i] = '_'; + } + + /* + * prepend ABST_PRFX to file name, minus the + * leading NULL character. This places the + * socket as a hidden file in the /tmp + * directory. + */ + (void) memmove(addr->sa_data + ABST_PRFX_LEN, + addr->sa_data + 1, orig_len); + bcopy(ABST_PRFX, addr->sa_data, ABST_PRFX_LEN); + + /* + * Since abstract socket paths may not be NULL + * terminated, we must explicitly NULL terminate + * our string. + */ + addr->sa_data[orig_len + ABST_PRFX_LEN] = '\0'; + + /* + * Make len reflect the new len of our string. + * Although we removed the NULL character at the + * beginning of the string, we added a NULL + * character to the end, so the net gain in + * length is simply ABST_PRFX_LEN. + */ + *len = inlen + ABST_PRFX_LEN; + } + break; + + case AF_PACKET: + sll = (struct sockaddr_ll *)addr; + if ((proto = convert_pkt_proto(sll->sll_protocol)) < 0) + return (-EINVAL); + sll->sll_protocol = proto; + *len = inlen; + break; + + default: + *len = inlen; + } + + addr->sa_family = family; + return (0); +} + +static int +stol_sockaddr(struct sockaddr *addr, socklen_t *len, + struct sockaddr *inaddr, socklen_t inlen, socklen_t orig) +{ + int size = inlen; + + switch (inaddr->sa_family) { + case AF_INET: + if (inlen > sizeof (struct sockaddr)) + return (EINVAL); + break; + + case AF_INET6: + if (inlen != sizeof (struct sockaddr_in6)) + return (EINVAL); + /* + * The linux sockaddr_in6 is shorter than illumos. + * We just truncate the extra field on the way out + */ + size = (sizeof (lx_sockaddr_in6_t)); + inlen = (sizeof (lx_sockaddr_in6_t)); + break; + + case AF_UNIX: + if (inlen > sizeof (struct sockaddr_un)) + return (EINVAL); + break; + + case (sa_family_t)AF_NOTSUPPORTED: + return (EPROTONOSUPPORT); + + case (sa_family_t)AF_INVAL: + return (EAFNOSUPPORT); + + default: + break; + } + + inaddr->sa_family = STOL_FAMILY(inaddr->sa_family); + + /* + * If inlen is larger than orig, copy out the maximum amount of + * data possible and then update *len to indicate the actual + * size of all the data that it wanted to copy out. + */ + size = (orig > 0 && orig < size) ? orig : size; + + if (uucopy(inaddr, addr, size) < 0) + return (errno); + + if (uucopy(&inlen, len, sizeof (socklen_t)) < 0) + return (errno); + + return (0); +} + +static int +convert_sock_args(int in_dom, int in_type, int in_protocol, int *out_dom, + int *out_type, int *out_options, int *out_protocol) +{ + int domain, type, options; + + if (in_dom < 0 || in_type < 0 || in_protocol < 0) + return (-EINVAL); + + domain = LTOS_FAMILY(in_dom); + if (domain == AF_NOTSUPPORTED || domain == AF_UNSPEC) + return (-EAFNOSUPPORT); + if (domain == AF_INVAL) + return (-EINVAL); + + type = LTOS_SOCKTYPE(in_type & LX_SOCK_TYPE_MASK); + if (type == SOCK_NOTSUPPORTED) + return (-ESOCKTNOSUPPORT); + if (type == SOCK_INVAL) + return (-EINVAL); + + /* + * Linux does not allow the app to specify IP Protocol for raw + * sockets. Illumos does, so bail out here. + */ + if (domain == AF_INET && type == SOCK_RAW && in_protocol == IPPROTO_IP) + return (-ESOCKTNOSUPPORT); + + options = 0; + if (in_type & LX_SOCK_NONBLOCK) + options |= SOCK_NONBLOCK; + if (in_type & LX_SOCK_CLOEXEC) + options |= SOCK_CLOEXEC; + + /* + * The protocol definitions for PF_PACKET differ between Linux and + * illumos. + */ + if (domain == PF_PACKET && + (in_protocol = convert_pkt_proto(in_protocol)) < 0) + return (EINVAL); + + *out_dom = domain; + *out_type = type; + *out_options = options; + *out_protocol = in_protocol; + return (0); +} + +static int +convert_sockflags(int lx_flags, char *call) +{ + int native_flags = 0; + + if (lx_flags & LX_MSG_OOB) { + native_flags |= MSG_OOB; + lx_flags &= ~LX_MSG_OOB; + } + + if (lx_flags & LX_MSG_PEEK) { + native_flags |= MSG_PEEK; + lx_flags &= ~LX_MSG_PEEK; + } + + if (lx_flags & LX_MSG_DONTROUTE) { + native_flags |= MSG_DONTROUTE; + lx_flags &= ~LX_MSG_DONTROUTE; + } + + if (lx_flags & LX_MSG_CTRUNC) { + native_flags |= MSG_CTRUNC; + lx_flags &= ~LX_MSG_CTRUNC; + } + + if (lx_flags & LX_MSG_PROXY) { + lx_unsupported("%s: unsupported socket flag MSG_PROXY", call); + lx_flags &= ~LX_MSG_PROXY; + } + + if (lx_flags & LX_MSG_TRUNC) { + native_flags |= MSG_TRUNC; + lx_flags &= ~LX_MSG_TRUNC; + } + + if (lx_flags & LX_MSG_DONTWAIT) { + native_flags |= MSG_DONTWAIT; + lx_flags &= ~LX_MSG_DONTWAIT; + } + + if (lx_flags & LX_MSG_EOR) { + native_flags |= MSG_EOR; + lx_flags &= ~LX_MSG_EOR; + } + + if (lx_flags & LX_MSG_WAITALL) { + native_flags |= MSG_WAITALL; + lx_flags &= ~LX_MSG_WAITALL; + } + + if (lx_flags & LX_MSG_FIN) { + lx_unsupported("%s: unsupported socket flag MSG_FIN", call); + lx_flags &= ~LX_MSG_FIN; + } + + if (lx_flags & LX_MSG_SYN) { + lx_unsupported("%s: unsupported socket flag MSG_SYN", call); + lx_flags &= ~LX_MSG_SYN; + } + + if (lx_flags & LX_MSG_CONFIRM) { + /* + * See the Linux arp.7 and sendmsg.2 man pages. We can ignore + * this option. + */ + lx_flags &= ~LX_MSG_CONFIRM; + } + + if (lx_flags & LX_MSG_RST) { + lx_unsupported("%s: unsupported socket flag MSG_RST", call); + lx_flags &= ~LX_MSG_RST; + } + + if (lx_flags & LX_MSG_ERRQUEUE) { + lx_unsupported("%s: unsupported socket flag MSG_ERRQUEUE", + call); + lx_flags &= ~LX_MSG_ERRQUEUE; + } + + if (lx_flags & LX_MSG_NOSIGNAL) { + /* MSG_NOSIGNAL handled within each caller */ + lx_flags &= ~LX_MSG_NOSIGNAL; + } + + if (lx_flags & LX_MSG_MORE) { + lx_unsupported("%s: unsupported socket flag MSG_MORE", call); + lx_flags &= ~LX_MSG_MORE; + } + + if (lx_flags & LX_MSG_WAITFORONE) { + lx_unsupported("%s: unsupported socket flag MSG_WAITFORONE", + call); + lx_flags &= ~LX_MSG_WAITFORONE; + } + + if (lx_flags & LX_MSG_FASTOPEN) { + lx_unsupported("%s: unsupported socket flag MSG_FASTOPEN", + call); + lx_flags &= ~LX_MSG_FASTOPEN; + } + + if (lx_flags & LX_MSG_CMSG_CLOEXEC) { + lx_unsupported("%s: unsupported socket flag MSG_CMSG_CLOEXEC", + call); + lx_flags &= ~LX_MSG_CMSG_CLOEXEC; + } + + if (lx_flags != 0) + lx_unsupported("%s: unknown socket flag(s) 0x%x", call, + lx_flags); + + return (native_flags); +} + +long +lx_socket(int domain, int type, int protocol) +{ + int options; + int fd; + int err; + + err = convert_sock_args(domain, type, protocol, + &domain, &type, &options, &protocol); + if (err != 0) + return (err); + + lx_debug("\tsocket(%d, %d, %d)", domain, type, protocol); + + fd = socket(domain, type | options, protocol); + + if (fd >= 0) + return (fd); + + if (errno == EPROTONOSUPPORT) + return (-ESOCKTNOSUPPORT); + + return (-errno); +} + +long +lx_bind(int sockfd, void *np, int nl) +{ + struct stat64 statbuf; + struct sockaddr *name; + socklen_t len; + int r, r2, ret, tmperrno; + int nlen; + lx_addr_type_t type; + struct stat sb; + + if ((nlen = calc_addr_size(np, nl, &type)) < 0) + return (nlen); + + if ((name = SAFE_ALLOCA(nlen)) == NULL) + return (-EINVAL); + bzero(name, nlen); + + if ((r = ltos_sockaddr(name, &len, np, nl, type)) < 0) + return (r); + + /* + * There are two types of Unix domain sockets for which we need to + * do some special handling with respect to bind: abstract namespace + * sockets and /dev/log. Abstract namespace sockets are simply Unix + * domain sockets that do not exist on the filesystem; we emulate them + * by changing their paths in ltos_sockaddr() to point to real + * file names in the filesystem. /dev/log is a special Unix domain + * socket that is used for system logging. On us, /dev isn't writable, + * so we rewrite these sockets in ltos_sockaddr() to point to a + * writable file (defined by LX_DEV_LOG_REDIRECT). In both cases, we + * introduce a new problem with respect to cleanup: abstract namespace + * sockets don't need to be cleaned up (when they are closed they are + * removed) and /dev/log can't be cleaned up because it's in the + * non-writable /dev. We solve these problems by cleaning up here in + * lx_bind(): before we create the socket, we check to see if it + * exists. If it does, we attempt to connect to it to see if it is in + * use, or just left over from a previous lx_bind() call. If we are + * unable to connect, we assume it is not in use and remove the file, + * then continue on as if the file never existed. + */ + if ((type == lxa_abstract || type == lxa_devlog) && + stat(name->sa_data, &sb) == 0 && S_ISSOCK(sb.st_mode)) { + if ((r2 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return (-ENOSR); + ret = connect(r2, name, len); + tmperrno = errno; + if (close(r2) < 0) + return (-EINVAL); + + /* + * if we can't connect to the socket, assume no one is using it + * and remove it, otherwise assume it is in use and return + * EADDRINUSE. + */ + if ((ret < 0) && (tmperrno == ECONNREFUSED)) { + if (unlink(name->sa_data) < 0) { + return (-EADDRINUSE); + } + } else { + return (-EADDRINUSE); + } + } + + lx_debug("\tbind(%d, 0x%p, %d)", sockfd, name, len); + + if (name->sa_family == AF_UNIX) + lx_debug("\t\tAF_UNIX, path = %s", name->sa_data); + + r = bind(sockfd, name, len); + + /* + * Linux returns EADDRINUSE for attempts to bind to Unix domain + * sockets that aren't sockets. + */ + if ((r < 0) && (errno == EINVAL) && (name->sa_family == AF_UNIX) && + ((stat64(name->sa_data, &statbuf) == 0) && + (!S_ISSOCK(statbuf.st_mode)))) + return (-EADDRINUSE); + + return ((r < 0) ? -errno : r); +} + +long +lx_connect(int sockfd, void *np, int nl) +{ + struct sockaddr *name; + socklen_t len; + int r; + int nlen; + lx_addr_type_t type; + + if ((nlen = calc_addr_size(np, nl, &type)) < 0) + return (nlen); + + if ((name = SAFE_ALLOCA(nlen)) == NULL) + return (-EINVAL); + bzero(name, nlen); + + if ((r = ltos_sockaddr(name, &len, np, nl, type)) < 0) + return (r); + + lx_debug("\tconnect(%d, 0x%p, %d)", sockfd, name, len); + + if (name->sa_family == AF_UNIX) + lx_debug("\t\tAF_UNIX, path = %s", name->sa_data); + + r = connect(sockfd, name, len); + + return ((r < 0) ? -errno : r); +} + +long +lx_listen(int sockfd, int backlog) +{ + int r; + + lx_debug("\tlisten(%d, %d)", sockfd, backlog); + r = listen(sockfd, backlog); + + return ((r < 0) ? -errno : r); +} + +long +lx_accept(int sockfd, void *name, int *nlp) +{ + socklen_t namelen = 0, origlen; + struct sockaddr *saddr; + int r, err; + int size; + + lx_debug("\taccept(%d, 0x%p, 0x%p", sockfd, (struct sockaddr *)name, + nlp); + + /* + * The Linux man page says that -1 is returned and errno is set to + * EFAULT if the "name" address is bad, but it is silent on what to + * set errno to if the "namelen" address is bad. Experimentation + * shows that Linux (at least the 2.4.21 kernel in CentOS) actually + * sets errno to EINVAL in both cases. + * + * Note that we must first check the name pointer, as the Linux + * docs state nothing is copied out if the "name" pointer is NULL. + * If it is NULL, we don't care about the namelen pointer's value + * or about dereferencing it. + * + * Happily, illumos' accept(3SOCKET) treats NULL name pointers and + * zero namelens the same way. + */ + if ((name != NULL) && + (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0)) + return ((errno == EFAULT) ? -EINVAL : -errno); + origlen = namelen; + + if (name != NULL) { + /* + * Use sizeof (struct sockaddr_in6) as the minimum temporary + * name allocation. This will allow families such as AF_INET6 + * to work properly when their namelen differs between LX and + * illumos. + */ + size = sizeof (struct sockaddr_in6); + if (namelen > size) + size = namelen; + + saddr = SAFE_ALLOCA(size); + if (saddr == NULL) + return (-EINVAL); + bzero(saddr, size); + } else { + saddr = NULL; + } + + lx_debug("\taccept namelen = %d", namelen); + + if ((r = accept(sockfd, saddr, &namelen)) < 0) + return ((errno == EFAULT) ? -EINVAL : -errno); + + lx_debug("\taccept namelen returned %d bytes", namelen); + + /* + * In Linux, accept()ed sockets do not inherit anything set by + * fcntl(), so filter those out. + */ + if (fcntl(r, F_SETFL, 0) < 0) + return (-errno); + + /* + * Once again, a bad "namelen" address sets errno to EINVAL, not + * EFAULT. If namelen was zero, there's no need to copy a zero back + * out. + * + * Logic might dictate that we should check if we can write to + * the namelen pointer earlier so we don't accept a pending connection + * only to fail the call because we can't write the namelen value back + * out. However, testing shows Linux does indeed fail the call after + * accepting the connection so we must behave in a compatible manner. + */ + if ((name != NULL) && (namelen != 0)) { + err = stol_sockaddr((struct sockaddr *)name, (socklen_t *)nlp, + saddr, namelen, origlen); + if (err != 0) { + close(r); + return ((err == EFAULT) ? -EINVAL : -err); + } + } + + return (r); +} + +long +lx_getsockname(int sockfd, void *np, int *nlp) +{ + struct sockaddr *name = NULL; + socklen_t namelen, namelen_orig; + struct stat sb; + int err; + + if (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0) + return (-errno); + namelen_orig = namelen; + + lx_debug("\tgetsockname(%d, 0x%p, 0x%p (=%d))", sockfd, + (struct sockaddr *)np, nlp, namelen); + + if (fstat(sockfd, &sb) == 0 && !S_ISSOCK(sb.st_mode)) + return (-ENOTSOCK); + + /* + * Use sizeof (struct sockaddr_in6) as the minimum temporary + * name allocation. This will allow families such as AF_INET6 + * to work properly when their namelen differs between LX and + * illumos. + */ + if (namelen <= 0) + return (-EBADF); + else if (namelen < sizeof (struct sockaddr_in6)) + namelen = sizeof (struct sockaddr_in6); + + if ((name = SAFE_ALLOCA(namelen)) == NULL) + return (-ENOMEM); + bzero(name, namelen); + + if (getsockname(sockfd, name, &namelen) < 0) + return (-errno); + + /* + * If the name that getsockname() wants to return is larger + * than namelen, getsockname() will copy out the maximum amount + * of data possible and then update namelen to indicate the + * actually size of all the data that it wanted to copy out. + */ + err = stol_sockaddr((struct sockaddr *)np, (socklen_t *)nlp, name, + namelen, namelen_orig); + return ((err != 0) ? -err : 0); +} + +long +lx_getpeername(int sockfd, void *np, int *nlp) +{ + struct sockaddr *name; + socklen_t namelen, namelen_orig; + int err; + + if (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0) + return (-errno); + namelen_orig = namelen; + + lx_debug("\tgetpeername(%d, 0x%p, 0x%p (=%d))", sockfd, + (struct sockaddr *)np, nlp, namelen); + + /* LTP can pass -1 but we'll limit the allocation to a page */ + if ((uint32_t)namelen > 4096) + return (-EINVAL); + + /* + * Linux returns EFAULT in this case, even if the namelen parameter + * is 0 (some test cases use -1, so we check for that too). This check + * will not catch other illegal addresses, but the benefit catching a + * non-null illegal address here is not worth the cost of another + * system call. + */ + if (np == NULL || np == (void *)-1) + return (-EFAULT); + + /* + * Use sizeof (struct sockaddr_in6) as the minimum temporary + * name allocation. This will allow families such as AF_INET6 + * to work properly when their namelen differs between LX and + * illumos. + */ + if (namelen < sizeof (struct sockaddr_in6)) + namelen = sizeof (struct sockaddr_in6); + + name = SAFE_ALLOCA(namelen); + if (name == NULL) + return (-EINVAL); + bzero(name, namelen); + + if ((getpeername(sockfd, name, &namelen)) < 0) + return (-errno); + + err = stol_sockaddr((struct sockaddr *)np, (socklen_t *)nlp, + name, namelen, namelen_orig); + if (err != 0) + return (-err); + + return (0); +} + +long +lx_socketpair(int domain, int type, int protocol, int *sv) +{ + int options; + int fds[2]; + int r; + + r = convert_sock_args(domain, type, protocol, &domain, &type, &options, + &protocol); + if (r != 0) + return (r); + + lx_debug("\tsocketpair(%d, %d, %d, 0x%p)", domain, type, protocol, sv); + + r = socketpair(domain, type | options, protocol, fds); + + if (r == 0) { + if (uucopy(fds, sv, sizeof (fds)) != 0) { + r = errno; + (void) close(fds[0]); + (void) close(fds[1]); + return (-r); + } + return (0); + } + + if (errno == EPROTONOSUPPORT) + return (-ESOCKTNOSUPPORT); + + return (-errno); +} + +long +lx_sendto(int sockfd, void *buf, size_t len, int flags, void *lto, int tolen) +{ + struct sockaddr *to = NULL; + ssize_t r; + socklen_t tlen = (socklen_t)tolen; + int nlen; + lx_addr_type_t type; + + int nosigpipe = flags & LX_MSG_NOSIGNAL; + struct sigaction newact, oact; + + if (lto != NULL) { + if (tolen < 0) + return (-EINVAL); + + if ((nlen = calc_addr_size(lto, tolen, &type)) < 0) + return (nlen); + + if ((to = SAFE_ALLOCA(nlen)) == NULL) + return (-EINVAL); + bzero(to, nlen); + + if ((r = ltos_sockaddr(to, &tlen, lto, tlen, type)) < 0) + return (r); + } + + + lx_debug("\tsendto(%d, 0x%p, 0x%d, 0x%x, 0x%x, %d)", sockfd, buf, len, + flags, to, tlen); + + flags = convert_sockflags(flags, "sendto"); + + /* + * If nosigpipe is set, we want to emulate the Linux action of + * not sending a SIGPIPE to the caller if the remote socket has + * already been closed. + * + * As SIGPIPE is a directed signal sent only to the thread that + * performed the action, we can emulate this behavior by momentarily + * resetting the action for SIGPIPE to SIG_IGN, performing the socket + * call, and resetting the action back to its previous value. + */ + if (nosigpipe) { + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + (void) sigemptyset(&newact.sa_mask); + + if (sigaction(SIGPIPE, &newact, &oact) < 0) + lx_err_fatal("sendto(): could not ignore SIGPIPE to " + "emulate LX_MSG_NOSIGNAL"); + } + + r = sendto(sockfd, buf, len, flags, to, tlen); + + if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0)) + lx_err_fatal("sendto(): could not reset SIGPIPE handler to " + "emulate LX_MSG_NOSIGNAL"); + + if (r < 0) { + /* + * according to the man page and LTP, the expected error in + * this case is EPIPE. + */ + if (errno == ENOTCONN) + return (-EPIPE); + else + return (-errno); + } + return (r); +} + +long +lx_recvfrom(int sockfd, void *buf, size_t len, int flags, void *from, + socklen_t *from_lenp) +{ + + struct sockaddr *orig_name = NULL; + struct sockaddr sname; + socklen_t nlen, orig_len = 0; + + ssize_t r, err; + + int nosigpipe = flags & LX_MSG_NOSIGNAL; + struct sigaction newact, oact; + + lx_debug("\trecvfrom(%d, 0x%p, 0x%d, 0x%x, 0x%p, 0x%p)", sockfd, buf, + len, flags, from, from_lenp); + + /* LTP expects EINVAL when from_len == -1 */ + if (from_lenp != NULL) { + if (uucopy(from_lenp, &nlen, sizeof (nlen)) != 0) + return (-errno); + if (nlen == -1) + return (-EINVAL); + } + + /* + * Allocate a temporary buffer for msg_name. This is to account for + * sockaddrs that differ in size between LX and illumos. + */ + if (from != NULL) { + orig_len = nlen; + orig_name = from; + nlen = sizeof (struct sockaddr); + if (getsockname(sockfd, &sname, &nlen) < 0) + nlen = sizeof (struct sockaddr); + if ((from = SAFE_ALLOCA(nlen)) == NULL) + return (-ENOMEM); + bzero(from, nlen); + } + + /* + * LTP sometimes passes -1 for the flags but expects a different + * failure result for something else that is wrong. + */ + if (flags != -1 && flags & LX_MSG_ERRQUEUE) + return (-EAGAIN); + flags = convert_sockflags(flags, "recvfrom"); + + /* + * If nosigpipe is set, we want to emulate the Linux action of + * not sending a SIGPIPE to the caller if the remote socket has + * already been closed. + * + * As SIGPIPE is a directed signal sent only to the thread that + * performed the action, we can emulate this behavior by momentarily + * resetting the action for SIGPIPE to SIG_IGN, performing the socket + * call, and resetting the action back to its previous value. + */ + if (nosigpipe) { + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + (void) sigemptyset(&newact.sa_mask); + + if (sigaction(SIGPIPE, &newact, &oact) < 0) + lx_err_fatal("recvfrom(): could not ignore SIGPIPE " + "to emulate LX_MSG_NOSIGNAL"); + } + + r = recvfrom(sockfd, buf, len, flags, (struct sockaddr *)from, + &nlen); + + if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0)) + lx_err_fatal("recvfrom(): could not reset SIGPIPE handler to " + "emulate LX_MSG_NOSIGNAL"); + + /* Copy out the locally buffered name/len, if needed */ + if (orig_name != NULL) { + err = stol_sockaddr(orig_name, from_lenp, + (struct sockaddr *)from, nlen, orig_len); + if (err != 0) + return (-err); + } + + return ((r < 0) ? -errno : r); +} + +long +lx_shutdown(int sockfd, int how) +{ + int r; + + lx_debug("\tshutdown(%d, %d)", sockfd, how); + r = shutdown(sockfd, how); + + return ((r < 0) ? -errno : r); +} + +static lx_proto_opts_t * +get_proto_opt_tbl(int level) +{ + switch (level) { + case LX_IPPROTO_IP: return (&ip_sockopts_tbl); + case LX_SOL_SOCKET: return (&socket_sockopts_tbl); + case LX_IPPROTO_IGMP: return (&igmp_sockopts_tbl); + case LX_IPPROTO_TCP: return (&tcp_sockopts_tbl); + case LX_IPPROTO_IPV6: return (&ipv6_sockopts_tbl); + case LX_IPPROTO_ICMPV6: return (&icmpv6_sockopts_tbl); + case LX_IPPROTO_RAW: return (&raw_sockopts_tbl); + case LX_SOL_PACKET: return (&packet_sockopts_tbl); + case LX_SOL_NETLINK: return (&netlink_sockopts_tbl); + default: + lx_unsupported("Unsupported sockopt level %d", level); + return (NULL); + } +} + +long +lx_setsockopt(int sockfd, int level, int optname, void *optval, int optlen) +{ + int r; + lx_proto_opts_t *proto_opts; + boolean_t converted = B_FALSE; + + lx_debug("\tsetsockopt(%d, %d, %d, 0x%p, %d)", sockfd, level, optname, + optval, optlen); + + /* + * The kernel returns EFAULT for all invalid addresses except NULL, + * for which it returns EINVAL. Linux wants EFAULT for NULL too. + */ + if (optval == NULL) + return (-EFAULT); + + if (level > LX_SOL_NETLINK || level == LX_IPPROTO_UDP) + return (-ENOPROTOOPT); + + if ((proto_opts = get_proto_opt_tbl(level)) == NULL) + return (-ENOPROTOOPT); + + if (optname <= 0 || optname >= proto_opts->maxentries) { + lx_unsupported("Unsupported sockopt %d, proto %d", optname, + level); + return (-ENOPROTOOPT); + } + + if (level == LX_IPPROTO_IP) { + /* + * Ping sets this option to receive errors on raw sockets. + * Currently we just ignore it to make ping happy. From the + * Linux ip.7 man page: + * For raw sockets, IP_RECVERR enables passing of all + * received ICMP errors to the application. + */ + if (optname == LX_IP_RECVERR && + strcmp(lx_cmd_name, "ping") == 0) + return (0); + + if (optname == LX_IP_RECVERR && + strcmp(lx_cmd_name, "traceroute") == 0) + return (0); + + if (optname == LX_IP_MTU_DISCOVER) { + /* + * Native programs such as traceroute use IP_DONTFRAG + * instead. Set that and ignore this flag. + */ + optname = IP_DONTFRAG; + converted = B_TRUE; + } + + /* + * For IP_MULTICAST_TTL and IP_MULTICAST_LOOP, Linux defines + * the option value to be an integer while we define it to be + * an unsigned character. To prevent the kernel from spitting + * back an error on an illegal length, verify that the option + * value is less than UCHAR_MAX before truncating optlen. + */ + if (optname == LX_IP_MULTICAST_TTL || + optname == LX_IP_MULTICAST_LOOP) { + int optcopy = 0; + + if (optlen > sizeof (int) || optlen <= 0) + return (-EINVAL); + + if (uucopy(optval, &optcopy, optlen) != 0) + return (-errno); + + if (optcopy > UCHAR_MAX) + return (-EINVAL); + + /* + * With optval validated, only optlen must be changed. + */ + optlen = sizeof (uchar_t); + } + } else if (level == LX_IPPROTO_IPV6) { + /* + * There isn't a good translation for IPV6_MTU and certain apps + * such as bind9 will bail if it cannot be set. We just lie + * about the success for now. + */ + if (optname == LX_IPV6_MTU) + return (0); + } else if (level == LX_IPPROTO_ICMPV6) { + if (optname == LX_ICMP6_FILTER && optval != NULL) { + int i; + icmp6_filter_t *filter; + /* + * Surprise! Linux's ICMP6_FILTER is inverted, when + * compared to illumos + */ + if (optlen != sizeof (icmp6_filter_t)) + return (-EINVAL); + if ((filter = SAFE_ALLOCA(optlen)) == NULL) + return (-ENOMEM); + if (uucopy(optval, filter, optlen) != 0) + return (-EFAULT); + for (i = 0; i < 8; i++) + filter->__icmp6_filt[i] ^= 0xffffffff; + optval = filter; + } + } else if (level == LX_IPPROTO_TCP && optname == LX_TCP_DEFER_ACCEPT) { + /* + * Emulate TCP_DEFER_ACCEPT using the datafilt(7M) socket + * filter but we can't emulate the timeout aspect so treat any + * non-zero value as enabling and zero as disabling. + */ + int val; + + if (optlen != sizeof (val)) + return (-EINVAL); + if (uucopy(optval, &val, optlen) != 0) + return (-EFAULT); + if (val < 0) + return (-EINVAL); + + if (val > 0) { + if (setsockopt(sockfd, SOL_FILTER, FIL_ATTACH, + "datafilt", 9) < 0) { + if (errno != EEXIST) + return (-errno); + } + } else { + if (setsockopt(sockfd, SOL_FILTER, FIL_DETACH, + "datafilt", 9) < 0) { + if (errno != ENXIO) + return (-errno); + } + } + return (0); + } else if (level == LX_SOL_SOCKET) { + /* Linux ignores this option. */ + if (optname == LX_SO_BSDCOMPAT) + return (0); + + /* Convert bpf program struct */ + if (optname == LX_SO_ATTACH_FILTER) { + struct lx_bpf_program *lbp; + struct bpf_program *bp; + if (optlen != sizeof (*lbp)) + return (-EINVAL); + if ((bp = SAFE_ALLOCA(sizeof (*bp))) == NULL || + (lbp = SAFE_ALLOCA(sizeof (*lbp))) == NULL) + return (-ENOMEM); + if (uucopy(optval, lbp, sizeof (*lbp)) != 0) + return (-errno); + bp->bf_len = lbp->bf_len; + bp->bf_insns = (struct bpf_insn *)lbp->bf_insns; + optval = bp; + } + + level = SOL_SOCKET; + } else if (level == LX_IPPROTO_RAW) { + /* + * Ping sets this option. Currently we just ignore it to make + * ping happy. + */ + if (optname == LX_ICMP_FILTER && + strcmp(lx_cmd_name, "ping") == 0) + return (0); + /* + * Ping6 tries to set the IPV6_CHECKSUM offset in a way that + * illumos won't allow. Quietly ignore this to prevent it from + * complaining. + */ + if (optname == LX_IPV6_CHECKSUM && + strcmp(lx_cmd_name, "ping6") == 0) + return (0); + } else if (level == LX_SOL_PACKET) { + level = SOL_PACKET; + if (optname == LX_PACKET_ADD_MEMBERSHIP || + optname == LX_PACKET_DROP_MEMBERSHIP) { + /* Convert Linux mr_type to illumos */ + struct packet_mreq *mr; + if (optlen != sizeof (*mr)) + return (-EINVAL); + mr = SAFE_ALLOCA(sizeof (*mr)); + if (uucopy(optval, mr, sizeof (*mr)) != 0) + return (-errno); + if (--mr->mr_type > PACKET_MR_ALLMULTI) + return (-EINVAL); + optval = mr; + } + } else if (level == LX_SOL_NETLINK) { + /* Just pass netlink options straight through */ + converted = B_TRUE; + } + + if (!converted) { + const lx_sockopt_map_t *mapping; + /* + * Do a table lookup of the Illumos equivalent of the given + * option. + */ + mapping = &proto_opts->proto[optname]; + if (mapping->lsm_opt == OPTNOTSUP) { + lx_unsupported("unsupported sockopt %d, proto %d", + optname, level); + return (-ENOPROTOOPT); + } + optname = mapping->lsm_opt; + /* Truncate the optlen if needed/allowed */ + if (mapping->lsm_lcap != 0 && optlen > mapping->lsm_lcap) { + optlen = mapping->lsm_lcap; + } + } + + r = setsockopt(sockfd, level, optname, optval, optlen); + + return ((r < 0) ? -errno : r); +} + +long +lx_getsockopt(int sockfd, int level, int optname, void *optval, int *optlenp) +{ + int r; + int orig_optname; + lx_proto_opts_t *proto_opts; + + lx_debug("\tgetsockopt(%d, %d, %d, 0x%p, 0x%p)", sockfd, level, optname, + optval, optlenp); + + /* + * According to the Linux man page, a NULL optval should indicate + * (as in illumos) that no return value is expected. Instead, it + * actually triggers an EFAULT error. + */ + if (optval == NULL) + return (-EFAULT); + + if (level > LX_SOL_PACKET || level == LX_IPPROTO_UDP) + return (-EOPNOTSUPP); + + if ((proto_opts = get_proto_opt_tbl(level)) == NULL) + return (-ENOPROTOOPT); + + if (optname <= 0 || optname >= (proto_opts->maxentries)) { + lx_unsupported("Unsupported sockopt %d, proto %d", optname, + level); + return (-ENOPROTOOPT); + } + + if (level == LX_IPPROTO_TCP) { + if (optname == LX_TCP_CORK) { + /* + * We don't support TCP_CORK but some apps rely on it. + * So, rather than return an error we just return 0. + * This isn't exactly a lie, since this option really + * isn't set, but it's not the whole truth either. + * Fortunately, we aren't under oath. + */ + r = 0; + if (uucopy(&r, optval, sizeof (int)) != 0) + return (-errno); + r = sizeof (int); + if (uucopy(&r, optlenp, sizeof (int)) != 0) + return (-errno); + return (0); + } else if (optname == LX_TCP_DEFER_ACCEPT) { + /* + * We do support TCP_DEFER_ACCEPT using the + * datafilt(7M) socket filter but we don't emulate the + * timeout aspect so treat the existence as 1 and + * absence as 0. + */ + struct fil_info fi[10]; + int i, tot, len, r; + + len = sizeof (fi); + if (getsockopt(sockfd, SOL_FILTER, FIL_LIST, fi, + &len) < 0) + return (-errno); + + tot = len / sizeof (struct fil_info); + r = 0; + for (i = 0; i < tot; i++) { + if (fi[i].fi_flags == FILF_PROG && + strcmp(fi[i].fi_name, "datafilt") == 0) { + r = 1; + break; + } + } + + if (uucopy(&r, optval, sizeof (int)) != 0) + return (-errno); + r = sizeof (int); + if (uucopy(&r, optlenp, sizeof (int)) != 0) + return (-errno); + return (0); + } + } + if ((level == LX_SOL_SOCKET) && (optname == LX_SO_PEERCRED)) { + struct lx_ucred lx_ucred; + ucred_t *ucp; + + /* + * We don't support SO_PEERCRED, but we do have equivalent + * functionality in getpeerucred() so invoke that here. + */ + + /* Verify there's going to be enough room for the results. */ + if (uucopy(optlenp, &r, sizeof (int)) != 0) + return (-errno); + if (r < sizeof (struct lx_ucred)) + return (-EOVERFLOW); + + /* + * We allocate a ucred_t ourselves rather than allow + * getpeerucred() to do it for us because getpeerucred() + * uses malloc(3C) and we'd rather use SAFE_ALLOCA(). + */ + if ((ucp = (ucred_t *)SAFE_ALLOCA(ucred_size())) == NULL) + return (-ENOMEM); + + /* Get the credential for the remote end of this socket. */ + if (getpeerucred(sockfd, &ucp) != 0) + return (-errno); + if (((lx_ucred.lxu_pid = ucred_getpid(ucp)) == -1) || + ((lx_ucred.lxu_uid = ucred_geteuid(ucp)) == (uid_t)-1) || + ((lx_ucred.lxu_gid = ucred_getegid(ucp)) == (gid_t)-1)) { + return (-errno); + } + + /* Copy out the results. */ + if ((uucopy(&lx_ucred, optval, sizeof (lx_ucred))) != 0) + return (-errno); + r = sizeof (lx_ucred); + if ((uucopy(&r, optlenp, sizeof (int))) != 0) + return (-errno); + return (0); + } + if ((level == LX_IPPROTO_ICMPV6) && (optname == LX_ICMP6_FILTER)) { + icmp6_filter_t *filter; + int i; + + /* Verify there's going to be enough room for the results. */ + if (uucopy(optlenp, &r, sizeof (int)) != 0) + return (-errno); + if (r < sizeof (icmp6_filter_t)) + return (-EINVAL); + if ((filter = SAFE_ALLOCA(sizeof (icmp6_filter_t))) == NULL) + return (-ENOMEM); + + r = getsockopt(sockfd, IPPROTO_ICMPV6, ICMP6_FILTER, filter, + optlenp); + if (r != 0) + return (-errno); + + /* + * ICMP6_FILTER is inverted on Linux. Make it so before copying + * back to caller's buffer. + */ + for (i = 0; i < 8; i++) + filter->__icmp6_filt[i] ^= 0xffffffff; + if ((uucopy(filter, optval, sizeof (icmp6_filter_t))) != 0) + return (-errno); + return (0); + } + if (level == LX_SOL_PACKET) + level = SOL_PACKET; + else if (level == LX_SOL_SOCKET) + level = SOL_SOCKET; + + orig_optname = optname; + + optname = proto_opts->proto[optname].lsm_opt; + if (optname == OPTNOTSUP) { + lx_unsupported("unsupported sockopt %d, proto %d", + orig_optname, level); + return (-ENOPROTOOPT); + } + + r = getsockopt(sockfd, level, optname, optval, optlenp); + + if (r == 0 && level == SOL_SOCKET) { + switch (optname) { + case SO_TYPE: + /* translate our type back to Linux */ + *(int *)optval = stol_socktype[(*(int *)optval)]; + break; + + case SO_ERROR: + *(int *)optval = lx_errno(*(int *)optval, -1); + break; + } + } + + return ((r < 0) ? -errno : r); +} + +/* + * libc routines that issue these system calls. We bypass the libsocket + * wrappers since they explicitly turn off the MSG_XPG_2 flag we need for + * Linux compatibility. + */ +extern int _so_sendmsg(); +extern int _so_recvmsg(); + +long +lx_sendmsg(int sockfd, void *lmp, int flags) +{ + struct lx_msghdr msg; + struct sockaddr *name = NULL; + struct cmsghdr *cmsg = NULL; + void *new_cmsg = NULL; + int r, size, res; + lx_addr_type_t type; + socklen_t len; + + int nosigpipe = flags & LX_MSG_NOSIGNAL; + struct sigaction newact, oact; + + lx_debug("\tsendmsg(%d, 0x%p, 0x%x)", sockfd, lmp, flags); + + flags = convert_sockflags(flags, "sendmsg"); + + if ((uucopy(lmp, &msg, sizeof (msg))) != 0) + return (-errno); + + /* + * Perform conversion on msg_name, if present. + */ + if (msg.msg_name != NULL) { + if (msg.msg_namelen < sizeof (struct sockaddr)) + return (-EINVAL); + size = calc_addr_size(msg.msg_name, msg.msg_namelen, &type); + if (size < 0) + return (size); + if ((name = malloc(size)) == NULL) { + res = -ENOMEM; + goto err; + } + bzero(name, size); + + if ((r = ltos_sockaddr(name, &len, msg.msg_name, + msg.msg_namelen, type)) < 0) { + res = r; + goto err; + } + msg.msg_name = name; + msg.msg_namelen = len; + } + + /* + * If there are control messages bundled in this message, we need + * to convert them from Linux to illumos. + */ + if (msg.msg_control != NULL) { + if (msg.msg_controllen == 0) { + cmsg = NULL; + } else { + cmsg = malloc(msg.msg_controllen); + if (cmsg == NULL) { + res = -EINVAL; + goto err; + } +#if defined(_LP64) + /* + * We don't know in advance how many control msgs + * there are, but we do know that the native header is + * 4 bytes smaller than the Linux header, so allocating + * the same size will over-estimate what we actually + * need. + */ + new_cmsg = malloc(msg.msg_controllen); + if (new_cmsg == NULL) { + res = -EINVAL; + goto err; + } +#endif + } + if ((uucopy(msg.msg_control, cmsg, msg.msg_controllen)) != 0) { + res = -errno; + goto err; + } + msg.msg_control = cmsg; + if ((r = convert_cmsgs(LX_TO_SOL, &msg, new_cmsg, + "sendmsg()")) != 0) { + res = -r; + goto err; + } + } + + /* + * If nosigpipe is set, we want to emulate the Linux action of + * not sending a SIGPIPE to the caller if the remote socket has + * already been closed. + * + * As SIGPIPE is a directed signal sent only to the thread that + * performed the action, we can emulate this behavior by momentarily + * resetting the action for SIGPIPE to SIG_IGN, performing the socket + * call, and resetting the action back to its previous value. + */ + if (nosigpipe) { + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + (void) sigemptyset(&newact.sa_mask); + + if (sigaction(SIGPIPE, &newact, &oact) < 0) + lx_err_fatal("sendmsg(): could not ignore SIGPIPE to " + "emulate LX_MSG_NOSIGNAL"); + } + + r = _so_sendmsg(sockfd, (struct msghdr *)&msg, flags | MSG_XPG4_2); + + if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0)) + lx_err_fatal("sendmsg(): could not reset SIGPIPE handler to " + "emulate LX_MSG_NOSIGNAL"); + + if (r < 0) { + /* + * according to the man page and LTP, the expected error in + * this case is EPIPE. + */ + if (errno == ENOTCONN) + res = -EPIPE; + else + res = -errno; + goto err; + } + + res = r; + +err: + if (name != NULL) + free(name); + if (cmsg != NULL) + free(cmsg); + if (new_cmsg != NULL) + free(new_cmsg); + return (res); +} + +long +lx_recvmsg(int sockfd, void *lmp, int flags) +{ + struct lx_msghdr msg; + struct sockaddr *name, *orig_name = NULL; + struct sockaddr sname; + struct cmsghdr *cmsg = NULL; + void *new_cmsg = NULL; + int r, err; + socklen_t len, orig_len = 0, controllen = 0; + void *msg_control = NULL; + + int nosigpipe = flags & LX_MSG_NOSIGNAL; + struct sigaction newact, oact; + + lx_debug("\trecvmsg(%d, 0x%p, 0x%x)", sockfd, lmp, flags); + + /* + * LTP sometimes passes -1 for the flags but expects a different + * failure result for something else that is wrong. + */ + if (flags != -1 && flags & LX_MSG_ERRQUEUE) + return (-EAGAIN); + flags = convert_sockflags(flags, "recvmsg"); + + if ((uucopy(lmp, &msg, sizeof (msg))) != 0) + return (-errno); + + /* + * Allocate a temporary buffer for msg_name. This is to account for + * sockaddrs that differ in size between LX and illumos. + */ + if (msg.msg_name != NULL) { + len = sizeof (struct sockaddr); + if (getsockname(sockfd, &sname, &len) < 0) + len = sizeof (struct sockaddr); + name = alloca(len); + orig_name = msg.msg_name; + orig_len = msg.msg_namelen; + msg.msg_name = name; + msg.msg_namelen = len; + } + + /* + * If we are expecting to have to convert any control messages, + * then we should receive them into our address space instead of + * the app's. + */ + if (msg.msg_control != NULL) { + cmsg = msg.msg_control; + if ((controllen = msg.msg_controllen) == 0) { + msg.msg_control = NULL; + } else { + /* + * Note that control message buffers can be quite + * long, e.g. 128KB or more. The native stack is + * not big enough for these two allocations so we + * use malloc(3C). + */ + lx_debug("\tmsg.msg_controllen = %d", + msg.msg_controllen); + if ((msg_control = malloc(msg.msg_controllen)) == + NULL) { + return (-ENOMEM); + } + msg.msg_control = msg_control; +#if defined(_LP64) + if ((new_cmsg = malloc(msg.msg_controllen + + LX_CMSG_EXTRA)) == NULL) { + free(msg_control); + return (-EINVAL); + } +#endif + } + } + + /* + * If nosigpipe is set, we want to emulate the Linux action of + * not sending a SIGPIPE to the caller if the remote socket has + * already been closed. + * + * As SIGPIPE is a directed signal sent only to the thread that + * performed the action, we can emulate this behavior by momentarily + * resetting the action for SIGPIPE to SIG_IGN, performing the socket + * call, and resetting the action back to its previous value. + */ + if (nosigpipe) { + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + (void) sigemptyset(&newact.sa_mask); + + if (sigaction(SIGPIPE, &newact, &oact) < 0) { + lx_err_fatal("recvmsg(): could not ignore SIGPIPE to " + "emulate LX_MSG_NOSIGNAL"); + } + } + + r = _so_recvmsg(sockfd, (struct msghdr *)&msg, flags | MSG_XPG4_2); + + if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0)) { + lx_err_fatal("recvmsg(): could not reset SIGPIPE handler to " + "emulate LX_MSG_NOSIGNAL"); + } + + if (r >= 0 && msg.msg_controllen >= sizeof (struct cmsghdr)) { + /* + * If there are control messages bundled in this message, we + * need to convert them from native illumos to Linux format. + */ + if ((err = convert_cmsgs(SOL_TO_LX, &msg, new_cmsg, + "recvmsg()")) != 0) { + free(msg_control); + free(new_cmsg); + return (-err); + } + + if (uucopy(msg.msg_control, cmsg, + MIN(controllen, msg.msg_controllen)) != 0) { + free(msg_control); + free(new_cmsg); + return (-errno); + } + } + + msg.msg_control = cmsg; + + /* Restore the name and namelen fields in the msghdr */ + if (msg.msg_name != NULL) { + err = stol_sockaddr(orig_name, &msg.msg_namelen, msg.msg_name, + msg.msg_namelen, orig_len); + if (err != 0) { + free(msg_control); + free(new_cmsg); + return (-err); + } + msg.msg_name = orig_name; + } + + /* + * A handful of the values in the msghdr are set by the recvmsg() + * call, so copy their values back to the caller. Rather than iterate, + * just copy the whole structure back. + */ + if (uucopy(&msg, lmp, sizeof (msg)) != 0) { + free(msg_control); + free(new_cmsg); + return (-errno); + } + + free(msg_control); + free(new_cmsg); + return ((r < 0) ? -errno : r); +} + +/* + * Based on the lx_accept code with the addition of the flags handling. + * See internal comments in that function for more explanation. + */ +long +lx_accept4(int sockfd, void *np, int *nlp, int lx_flags) +{ + socklen_t namelen, namelen_orig; + struct sockaddr *name = NULL; + int flags = 0; + int r, err; + + lx_debug("\taccept4(%d, 0x%p, 0x%p 0x%x", sockfd, np, nlp, lx_flags); + + if ((np != NULL) && + (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0)) + return ((errno == EFAULT) ? -EINVAL : -errno); + + namelen_orig = namelen; + lx_debug("\taccept4 namelen = %d", namelen); + + if (np != NULL) { + /* + * Use sizeof (struct sockaddr_in6) as the minimum temporary + * name allocation. This will allow families such as AF_INET6 + * to work properly when their namelen differs between LX and + * illumos. + */ + if (namelen < sizeof (struct sockaddr_in6)) + namelen = sizeof (struct sockaddr_in6); + + name = SAFE_ALLOCA(namelen); + if (name == NULL) + return (-EINVAL); + bzero(name, namelen); + } + + if (lx_flags & LX_SOCK_NONBLOCK) + flags |= SOCK_NONBLOCK; + + if (lx_flags & LX_SOCK_CLOEXEC) + flags |= SOCK_CLOEXEC; + + if ((r = accept4(sockfd, name, &namelen, flags)) < 0) + return ((errno == EFAULT) ? -EINVAL : -errno); + + lx_debug("\taccept4 namelen returned %d bytes", namelen); + + if (np != NULL && namelen != 0) { + err = stol_sockaddr((struct sockaddr *)np, (socklen_t *)nlp, + name, namelen, namelen_orig); + if (err != 0) { + close(r); + return ((err == EFAULT) ? -EINVAL : -err); + } + } + return (r); +} + +#ifdef __i386 + +static int +lx_socket32(ulong_t *args) +{ + return (lx_socket((int)args[0], (int)args[1], (int)args[2])); +} + +static int +lx_bind32(ulong_t *args) +{ + return (lx_bind((int)args[0], (struct sockaddr *)args[1], + (int)args[2])); +} + +static int +lx_connect32(ulong_t *args) +{ + return (lx_connect((int)args[0], (struct sockaddr *)args[1], + (int)args[2])); +} + +static int +lx_listen32(ulong_t *args) +{ + return (lx_listen((int)args[0], (int)args[1])); +} + +static int +lx_accept32(ulong_t *args) +{ + return (lx_accept((int)args[0], (struct sockaddr *)args[1], + (int *)args[2])); +} + +static int +lx_getsockname32(ulong_t *args) +{ + return (lx_getsockname((int)args[0], (struct sockaddr *)args[1], + (int *)args[2])); +} + +static int +lx_getpeername32(ulong_t *args) +{ + return (lx_getpeername((int)args[0], (struct sockaddr *)args[1], + (int *)args[2])); +} + +static int +lx_socketpair32(ulong_t *args) +{ + return (lx_socketpair((int)args[0], (int)args[1], (int)args[2], + (int *)args[3])); +} + +static ssize_t +lx_send(ulong_t *args) +{ + int sockfd = (int)args[0]; + void *buf = (void *)args[1]; + size_t len = (size_t)args[2]; + int flags = (int)args[3]; + ssize_t r; + + int nosigpipe = flags & LX_MSG_NOSIGNAL; + struct sigaction newact, oact; + + lx_debug("\tsend(%d, 0x%p, 0x%d, 0x%x)", sockfd, buf, len, flags); + + flags = convert_sockflags(flags, "send"); + + /* + * If nosigpipe is set, we want to emulate the Linux action of + * not sending a SIGPIPE to the caller if the remote socket has + * already been closed. + * + * As SIGPIPE is a directed signal sent only to the thread that + * performed the action, we can emulate this behavior by momentarily + * resetting the action for SIGPIPE to SIG_IGN, performing the socket + * call, and resetting the action back to its previous value. + */ + if (nosigpipe) { + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + (void) sigemptyset(&newact.sa_mask); + + if (sigaction(SIGPIPE, &newact, &oact) < 0) + lx_err_fatal("send(): could not ignore SIGPIPE to " + "emulate LX_MSG_NOSIGNAL"); + } + + r = send(sockfd, buf, len, flags); + + if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0)) + lx_err_fatal("send(): could not reset SIGPIPE handler to " + "emulate LX_MSG_NOSIGNAL"); + + return ((r < 0) ? -errno : r); +} + +static ssize_t +lx_recv(ulong_t *args) +{ + int sockfd = (int)args[0]; + void *buf = (void *)args[1]; + size_t len = (size_t)args[2]; + int flags = (int)args[3]; + ssize_t r; + + int nosigpipe = flags & LX_MSG_NOSIGNAL; + struct sigaction newact, oact; + + lx_debug("\trecv(%d, 0x%p, 0x%d, 0x%x)", sockfd, buf, len, flags); + + /* + * LTP sometimes passes -1 for the flags but expects a different + * failure result for something else that is wrong. + */ + if (flags != -1 && flags & LX_MSG_ERRQUEUE) + return (-EAGAIN); + flags = convert_sockflags(flags, "recv"); + + /* + * If nosigpipe is set, we want to emulate the Linux action of + * not sending a SIGPIPE to the caller if the remote socket has + * already been closed. + * + * As SIGPIPE is a directed signal sent only to the thread that + * performed the action, we can emulate this behavior by momentarily + * resetting the action for SIGPIPE to SIG_IGN, performing the socket + * call, and resetting the action back to its previous value. + */ + if (nosigpipe) { + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + (void) sigemptyset(&newact.sa_mask); + + if (sigaction(SIGPIPE, &newact, &oact) < 0) + lx_err_fatal("recv(): could not ignore SIGPIPE to " + "emulate LX_MSG_NOSIGNAL"); + } + + r = recv(sockfd, buf, len, flags); + + if ((nosigpipe) && (sigaction(SIGPIPE, &oact, NULL) < 0)) + lx_err_fatal("recv(): could not reset SIGPIPE handler to " + "emulate LX_MSG_NOSIGNAL"); + + return ((r < 0) ? -errno : r); +} + +static ssize_t +lx_sendto32(ulong_t *args) +{ + return (lx_sendto((int)args[0], (void *)args[1], (size_t)args[2], + (int)args[3], (struct sockaddr *)args[4], (int)args[5])); +} + +static ssize_t +lx_recvfrom32(ulong_t *args) +{ + return (lx_recvfrom((int)args[0], (void *)args[1], (size_t)args[2], + (int)args[3], (struct sockaddr *)args[4], (socklen_t *)args[5])); +} + +static int +lx_shutdown32(ulong_t *args) +{ + return (lx_shutdown((int)args[0], (int)args[1])); +} + +static int +lx_setsockopt32(ulong_t *args) +{ + return (lx_setsockopt((int)args[0], (int)args[1], (int)args[2], + (void *)args[3], (int)args[4])); +} + +static int +lx_getsockopt32(ulong_t *args) +{ + return (lx_getsockopt((int)args[0], (int)args[1], (int)args[2], + (void *)args[3], (int *)args[4])); +} + +static int +lx_sendmsg32(ulong_t *args) +{ + return (lx_sendmsg((int)args[0], (void *)args[1], (int)args[2])); +} + +static int +lx_recvmsg32(ulong_t *args) +{ + return (lx_recvmsg((int)args[0], (void *)args[1], (int)args[2])); +} + +static int +lx_accept4_32(ulong_t *args) +{ + return (lx_accept4((int)args[0], (struct sockaddr *)args[1], + (int *)args[2], (int)args[3])); +} + +static int +lx_recvmmsg32(ulong_t *args) +{ + lx_unsupported("Unsupported socketcall: recvmmsg\n."); + return (-EINVAL); +} + +static int +lx_sendmmsg32(ulong_t *args) +{ + lx_unsupported("Unsupported socketcall: sendmmsg\n."); + return (-EINVAL); +} + +long +lx_socketcall(uintptr_t p1, uintptr_t p2) +{ + int subcmd = (int)p1 - 1; /* subcommands start at 1 - not 0 */ + ulong_t args[6]; + int r; + + if (subcmd < 0 || subcmd >= LX_SENDMMSG) + return (-EINVAL); + + /* + * Copy the arguments to the subcommand in from the app's address + * space, returning EFAULT if we get a bogus pointer. + */ + if (uucopy((void *)p2, args, + sockfns[subcmd].s_nargs * sizeof (ulong_t))) + return (-errno); + + r = (sockfns[subcmd].s_fn)(args); + + return (r); +} + +#endif /* __i386 */ 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..e68c408ca4 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/stack.c @@ -0,0 +1,313 @@ +/* + * 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. + */ + +/* + * 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) +{ + /* + * 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. + * + * 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. + */ + _sigoff(); + VERIFY0(mutex_lock(&lx_stack_list_lock)); +} + +void +lx_stack_postfork(void) +{ + 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(); + + _sigoff(); + VERIFY0(mutex_lock(&lx_stack_list_lock)); + + 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)); + } + + VERIFY0(mutex_unlock(&lx_stack_list_lock)); + _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: %s", + strerror(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/stat.c b/usr/src/lib/brand/lx/lx_brand/common/stat.c new file mode 100644 index 0000000000..7c2b5008f6 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/stat.c @@ -0,0 +1,552 @@ +/* + * 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 2014 Joyent, Inc. All rights reserved. + */ + +/* + * when a stat() is done for a non-device file, the devt returned + * via the stat is the devt of the device backing the filesystem which + * contains the file the stat was performed on. these devts are currently + * untranslated. if this turns out to cause problems in the future then + * we might want to add more devt translators to convert sd and cmdk + * devts into linux devts that normally represent disks. + * + * XXX this may not be the best place to have the devt translation code. + * devt translation will also be needed for /proc fs support, which will + * probably be done in the kernel. we may need to move this code into + * the kernel and add a brand syscall to do the translation for us. this + * will need to be worked out before putback. + */ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <libintl.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/lx_types.h> +#include <sys/lx_stat.h> +#include <sys/lx_misc.h> +#include <sys/lx_debug.h> +#include <sys/lx_ptm.h> +#include <sys/lx_audio.h> +#include <sys/lx_fcntl.h> +#include <sys/lx_syscall.h> +#include <sys/lx_debug.h> +#include <sys/modctl.h> + +/* define _KERNEL to get the devt manipulation macros */ +#define _KERNEL +#include <sys/sysmacros.h> +#undef _KERNEL + + +#define LX_PTS_MAJOR_MIN 136 +#define LX_PTS_MAJOR_MAX 143 +#define LX_PTS_MAX \ + ((LX_PTS_MAJOR_MAX - LX_PTS_MAJOR_MIN + 1) * LX_MINORMASK) + +#define LX_PTM_MAJOR 5 +#define LX_PTM_MINOR 2 + +/* values for dt_type */ +#define DTT_INVALID 0 +#define DTT_LIST 1 +#define DTT_CUSTOM 2 + +/* convience macros for access the dt_minor union */ +#define dt_list dt_minor.dtm_list +#define dt_custom dt_minor.dtm_custom + +/* + * structure used to define devt translators + */ +typedef struct minor_translator { + char *mt_path; /* solaris minor node path */ + minor_t mt_minor; /* solaris minor node number */ + int mt_lx_major; /* linux major node number */ + int mt_lx_minor; /* linux minor node number */ +} minor_translator_t; + +typedef struct devt_translator { + char *dt_driver; /* solaris driver name */ + major_t dt_major; /* solaris driver number */ + + /* dt_type dictates how we intrepret dt_minor */ + int dt_type; + union { + uintptr_t dtm_foo; /* required to compile */ + minor_translator_t *dtm_list; + int (*dtm_custom)(dev_t, lx_dev_t *, int); + } dt_minor; +} devt_translator_t; + + +/* + * forward declerations + */ +static devt_translator_t devt_translators[]; + +/* + * called to initialize the devt translation subsystem + */ +int +lx_stat_init() +{ + minor_translator_t *mt; + struct stat st; + major_t major; + char *driver; + int i, j, ret; + + for (i = 0; devt_translators[i].dt_driver != NULL; i++) { + + assert(devt_translators[i].dt_type != DTT_INVALID); + + /* figure out the major numbers for our devt translators */ + driver = devt_translators[i].dt_driver; + ret = modctl(MODGETMAJBIND, + driver, strlen(driver) + 1, &major); + if (ret != 0) { + lx_err("lx_stat_init(): modctl(MODGETMAJBIND, %s) " + "failed: %s\n", driver, strerror(errno)); + lx_err("lx_stat_init(): devt translator disabled " + "for: %s\n", driver); + devt_translators[i].dt_major = (major_t)-1; + continue; + } + + /* save the major node value */ + devt_translators[i].dt_major = major; + + /* if this translator doesn't use a list mapping we're done. */ + if (devt_translators[i].dt_type != DTT_LIST) + continue; + + /* for each device listed, lookup the minor node number */ + mt = devt_translators[i].dt_list; + for (j = 0; mt[j].mt_path != NULL; j++) { + + /* stat the device */ + ret = stat(mt[j].mt_path, &st); + if (ret != 0) { + lx_err("lx_stat_init(): stat(%s) failed: %s\n", + mt[j].mt_path, strerror(errno)); + lx_err("lx_stat_init(): devt translator " + "disabled for: %s\n", mt[j].mt_path); + st.st_rdev = NODEV; + } else { + /* make sure the major node matches */ + assert(getmajor(st.st_rdev) == major); + assert(mt[j].mt_minor < LX_MINORMASK); + } + + /* save the minor node value */ + mt[j].mt_minor = getminor(st.st_rdev); + } + } + return (0); +} + +static int +/*ARGSUSED*/ +pts_devt_translator(dev_t dev, lx_dev_t *jdev, int fd) +{ + minor_t min = getminor(dev); + int lx_maj; + int lx_min; + + /* + * linux has a really small minor number name space (8 bits). + * so if pts devices are limited to one major number you could + * only have 256 of them. linux addresses this issue by using + * multiple major numbers for pts devices. + */ + if (min >= LX_PTS_MAX) + return (EOVERFLOW); + + lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MINORMASK); + lx_min = min % LX_MINORMASK; + + *jdev = LX_MAKEDEVICE(lx_maj, lx_min); + return (0); +} + + +static int +/*ARGSUSED*/ +ptm_devt_translator(dev_t dev, lx_dev_t *jdev, int fd) +{ + *jdev = LX_MAKEDEVICE(LX_PTM_MAJOR, LX_PTM_MINOR); + return (0); +} + +static int +audio_devt_translator(dev_t dev, lx_dev_t *jdev, int fd) +{ + int s_minor, l_minor; + + if (fd == -1) { + s_minor = getminor(dev); + } else { + /* + * this is a cloning device so we have to ask the driver + * what kind of minor node this is + */ + if (ioctl(fd, LXA_IOC_GETMINORNUM, &s_minor) < 0) + return (-EINVAL); + } + + switch (s_minor) { + case LXA_MINORNUM_DSP: + l_minor = 3; + break; + case LXA_MINORNUM_MIXER: + l_minor = 0; + break; + default: + return (-EINVAL); + } + + *jdev = LX_MAKEDEVICE(14, l_minor); + return (0); +} + +static void +s2l_dev_report(dev_t dev, lx_dev_t jdev) +{ + major_t maj; + minor_t min; + int lx_maj, lx_min; + + if (!LX_DEBUG_ISENABLED) + return; + + maj = getmajor(dev); + min = getminor(dev); + + lx_maj = LX_GETMAJOR(jdev); + lx_min = LX_GETMINOR(jdev); + + lx_debug("\ttranslated devt [%d, %d] -> [%d, %d]", + maj, min, lx_maj, lx_min); +} + +static int +s2l_devt(dev_t dev, lx_dev_t *jdev, int fd) +{ + minor_translator_t *mt; + int i, j, err; + major_t maj = getmajor(dev); + minor_t min = getminor(dev); + + /* look for a devt translator for this major number */ + for (i = 0; devt_translators[i].dt_driver != NULL; i++) { + if (devt_translators[i].dt_major == maj) + break; + } + if (devt_translators[i].dt_driver != NULL) { + + /* try to translate the solaris devt to a linux devt */ + switch (devt_translators[i].dt_type) { + case DTT_LIST: + mt = devt_translators[i].dt_list; + for (j = 0; mt[j].mt_path != NULL; j++) { + if (mt[j].mt_minor == min) { + assert(mt[j].mt_minor < LX_MINORMASK); + + /* found a translation */ + *jdev = LX_MAKEDEVICE( + mt[j].mt_lx_major, + mt[j].mt_lx_minor); + s2l_dev_report(dev, *jdev); + return (0); + } + } + break; + + case DTT_CUSTOM: + err = devt_translators[i].dt_custom(dev, jdev, fd); + if (err == 0) + s2l_dev_report(dev, *jdev); + return (err); + break; + } + } + + /* we don't have a translator for this device */ + *jdev = LX_MAKEDEVICE(maj, min); + return (0); +} + +static int +stat_convert(uintptr_t lx_statp, struct stat *s, int fd) +{ + struct lx_stat buf; + lx_dev_t st_dev, st_rdev; + int err; + + if ((err = s2l_devt(s->st_dev, &st_dev, fd)) != 0) + return (err); + if ((err = s2l_devt(s->st_rdev, &st_rdev, fd)) != 0) + return (err); + + if ((st_dev > USHRT_MAX) || (st_rdev > USHRT_MAX) || + (s->st_nlink > USHRT_MAX) || (s->st_size > LONG_MAX)) + return (-EOVERFLOW); + + /* Linux seems to report a 0 st_size for all block devices */ + if ((s->st_mode & S_IFMT) == S_IFBLK) + s->st_size = 0; + + bzero(&buf, sizeof (buf)); + buf.st_dev = st_dev; + buf.st_rdev = st_rdev; + buf.st_ino = (lx_ino_t)s->st_ino; + buf.st_mode = s->st_mode; + buf.st_nlink = s->st_nlink; + buf.st_uid = LX_UID32_TO_UID16(s->st_uid); + buf.st_gid = LX_GID32_TO_GID16(s->st_gid); + buf.st_size = (lx_off_t)s->st_size; + buf.st_blksize = (lx_blksize_t)s->st_blksize; + buf.st_blocks = s->st_blocks; + buf.st_atime.ts_sec = s->st_atim.tv_sec; + buf.st_atime.ts_nsec = s->st_atim.tv_nsec; + buf.st_ctime.ts_sec = s->st_ctim.tv_sec; + buf.st_ctime.ts_nsec = s->st_ctim.tv_nsec; + buf.st_mtime.ts_sec = s->st_mtim.tv_sec; + buf.st_mtime.ts_nsec = s->st_mtim.tv_nsec; + + if (uucopy(&buf, (void *)lx_statp, sizeof (buf)) != 0) + return (-errno); + + return (0); +} + +static int +stat64_convert(uintptr_t lx_statp, struct stat64 *s, int fd) +{ + struct lx_stat64 buf; + lx_dev_t st_dev, st_rdev; + int err; + + if ((err = s2l_devt(s->st_dev, &st_dev, fd)) != 0) + return (err); + if ((err = s2l_devt(s->st_rdev, &st_rdev, fd)) != 0) + return (err); + + /* Linux seems to report a 0 st_size for all block devices */ + if ((s->st_mode & S_IFMT) == S_IFBLK) + s->st_size = 0; + + bzero(&buf, sizeof (buf)); + buf.st_dev = st_dev; + buf.st_rdev = st_rdev; +#if defined(_ILP32) + buf.st_small_ino = (lx_ino_t)(s->st_ino & UINT_MAX); +#endif + buf.st_ino = (lx_ino64_t)s->st_ino; + buf.st_mode = s->st_mode; + buf.st_nlink = s->st_nlink; + buf.st_uid = s->st_uid; + buf.st_gid = s->st_gid; + buf.st_size = s->st_size; + buf.st_blksize = s->st_blksize; + buf.st_blocks = s->st_blocks; + buf.st_atime.ts_sec = s->st_atim.tv_sec; + buf.st_atime.ts_nsec = s->st_atim.tv_nsec; + buf.st_ctime.ts_sec = s->st_ctim.tv_sec; + buf.st_ctime.ts_nsec = s->st_ctim.tv_nsec; + buf.st_mtime.ts_sec = s->st_mtim.tv_sec; + buf.st_mtime.ts_nsec = s->st_mtim.tv_nsec; + + if (uucopy(&buf, (void *)lx_statp, sizeof (buf)) != 0) + return (-errno); + + return (0); +} + +long +lx_stat(uintptr_t p1, uintptr_t p2) +{ + char *path = (char *)p1; + struct stat sbuf; + + lx_debug("\tstat(%s, ...)", path); + if (stat(path, &sbuf)) + return (-errno); + + return (stat_convert(p2, &sbuf, -1)); +} + + +long +lx_fstat(uintptr_t p1, uintptr_t p2) +{ + int fd = (int)p1; + struct stat sbuf; + char *path, path_buf[MAXPATHLEN]; + + if (LX_DEBUG_ISENABLED) { + path = lx_fd_to_path(fd, path_buf, sizeof (path_buf)); + if (path == NULL) + path = "?"; + + lx_debug("\tfstat(%d - %s, ...)", fd, path); + } + if (fstat(fd, &sbuf)) + return (-errno); + + return (stat_convert(p2, &sbuf, fd)); +} + + +long +lx_lstat(uintptr_t p1, uintptr_t p2) +{ + char *path = (char *)p1; + struct stat sbuf; + + lx_debug("\tlstat(%s, ...)", path); + if (lstat(path, &sbuf)) + return (-errno); + + return (stat_convert(p2, &sbuf, -1)); +} + +long +lx_stat64(uintptr_t p1, uintptr_t p2) +{ + char *path = (char *)p1; + struct stat64 sbuf; + + lx_debug("\tstat64(%s, ...)", path); + if (stat64(path, &sbuf)) + return (-errno); + + return (stat64_convert(p2, &sbuf, -1)); +} + + +long +lx_fstat64(uintptr_t p1, uintptr_t p2) +{ + int fd = (int)p1; + struct stat64 sbuf; + char *path, path_buf[MAXPATHLEN]; + + if (lx_debug_enabled != 0) { + path = lx_fd_to_path(fd, path_buf, sizeof (path_buf)); + if (path == NULL) + path = "?"; + + lx_debug("\tfstat64(%d - %s, ...)", fd, path); + } + if (fstat64(fd, &sbuf)) + return (-errno); + + return (stat64_convert(p2, &sbuf, fd)); +} + +long +lx_fstatat64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) +{ + int atfd = (int)p1; + const char *path = (const char *)p2; + int flag; + struct stat64 sbuf; + + if (atfd == LX_AT_FDCWD) + atfd = AT_FDCWD; + + flag = ltos_at_flag(p4, AT_SYMLINK_NOFOLLOW, B_TRUE); + if (flag < 0) + return (-EINVAL); + + if (fstatat64(atfd, path, &sbuf, flag)) + return (-errno); + + return (stat64_convert(p3, &sbuf, -1)); +} + + +long +lx_lstat64(uintptr_t p1, uintptr_t p2) +{ + char *path = (char *)p1; + struct stat64 sbuf; + + lx_debug("\tlstat64(%s, ...)", path); + if (lstat64(path, &sbuf)) + return (-errno); + + return (stat64_convert(p2, &sbuf, -1)); +} + +/* + * devt translator definitions + */ +#define MINOR_TRANSLATOR(path, lx_major, lx_minor) \ + { path, 0, lx_major, lx_minor } + +#define MINOR_TRANSLATOR_END \ + { NULL, 0, 0, 0 } + +#define DEVT_TRANSLATOR(drv, flags, i) \ + { drv, 0, flags, (uintptr_t)i } + +/* + * translators for devts + */ +static minor_translator_t mtranslator_mm[] = { + MINOR_TRANSLATOR("/dev/null", 1, 3), + MINOR_TRANSLATOR("/dev/zero", 1, 5), + MINOR_TRANSLATOR_END +}; +static minor_translator_t mtranslator_random[] = { + MINOR_TRANSLATOR("/dev/random", 1, 8), + MINOR_TRANSLATOR("/dev/urandom", 1, 9), + MINOR_TRANSLATOR_END +}; +static minor_translator_t mtranslator_sy[] = { + MINOR_TRANSLATOR("/dev/tty", 5, 0), + MINOR_TRANSLATOR_END +}; +static minor_translator_t mtranslator_zcons[] = { + MINOR_TRANSLATOR("/dev/console", 5, 1), + MINOR_TRANSLATOR_END +}; +static devt_translator_t devt_translators[] = { + DEVT_TRANSLATOR("mm", DTT_LIST, &mtranslator_mm), + DEVT_TRANSLATOR("random", DTT_LIST, &mtranslator_random), + DEVT_TRANSLATOR("sy", DTT_LIST, &mtranslator_sy), + DEVT_TRANSLATOR("zcons", DTT_LIST, &mtranslator_zcons), + DEVT_TRANSLATOR(LX_AUDIO_DRV, DTT_CUSTOM, audio_devt_translator), + DEVT_TRANSLATOR(LX_PTM_DRV, DTT_CUSTOM, ptm_devt_translator), + DEVT_TRANSLATOR("pts", DTT_CUSTOM, pts_devt_translator), + DEVT_TRANSLATOR(NULL, 0, 0) +}; 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..b3ec94bdc8 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/statfs.c @@ -0,0 +1,345 @@ +/* + * 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 <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" }, + { 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..665d4ce0a7 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c @@ -0,0 +1,972 @@ +/* + * 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. + */ + +#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 + +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 = m.msgmnb; + 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); + + 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, IPC_RMID, NULL); + 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..c810aa33f1 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/common/time.c @@ -0,0 +1,132 @@ +/* + * 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 <time.h> +#include <string.h> +#include <strings.h> +#include <sys/times.h> +#include <sys/lx_syscall.h> +#include <sys/lx_misc.h> + +/* + * times() - The Linux implementation avoids writing to NULL, while Illumos + * returns EFAULT. + */ +long +lx_times(uintptr_t p1) +{ + clock_t ret; + struct tms buf, *tp = (struct tms *)p1; + + ret = times(&buf); + + if ((ret == -1) || + ((tp != NULL) && uucopy((void *)&buf, tp, sizeof (buf)) != 0)) + return (-errno); + + return ((ret == -1) ? -errno : ret); +} + +/* + * 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 != 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 != NULL) && + (uucopy((struct lx_timezone *)p2, &tz, sizeof (tz)) < 0)) + return (-errno); + + if ((p1 != 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..e9546dee4a --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/i386/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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# Copyright 2014 Joyent, Inc. All rights reserved. +# +# 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 + +$(ASSYMDEP_OBJS:%=pics/%): assym.h + +OFFSETS = ../$(MACH)/offsets.in + +assym.h: $(OFFSETS) + $(OFFSETS_CREATE) $(CTF_FLAGS) < $(OFFSETS) > $@ + +CLOBBERFILES += assym.h + +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..bce7f0005c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s @@ -0,0 +1,93 @@ +/* + * 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 */ + +#include "assym.h" + +/* 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/i386/offsets.in b/usr/src/lib/brand/lx/lx_brand/i386/offsets.in new file mode 100644 index 0000000000..ac934ee76c --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/i386/offsets.in @@ -0,0 +1,40 @@ +\ +\ 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 +\ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/lx_brand.h> + +lx_regs_t SIZEOF_LX_REGS_T + lxr_gs + lxr_edi + lxr_esi + lxr_ebp + lxr_esp + lxr_ebx + lxr_edx + lxr_ecx + lxr_eax + lxr_eip + lxr_orig_eax diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_aio.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_aio.h new file mode 100644 index 0000000000..c2b41530e7 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_aio.h @@ -0,0 +1,81 @@ +/* + * 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. All rights reserved. + */ + +#ifndef _SYS_LX_AIO_H +#define _SYS_LX_AIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LX_IOCB_FLAG_RESFD 0x0001 + +#define LX_IOCB_CMD_PREAD 0 +#define LX_IOCB_CMD_PWRITE 1 +#define LX_IOCB_CMD_FSYNC 2 +#define LX_IOCB_CMD_FDSYNC 3 +#define LX_IOCB_CMD_PREADX 4 +#define LX_IOCB_CMD_POLL 5 +#define LX_IOCB_CMD_NOOP 6 +#define LX_IOCB_CMD_PREADV 7 +#define LX_IOCB_CMD_PWRITEV 8 + +#define LX_KIOCB_KEY 0 + +typedef struct lx_io_event lx_io_event_t; +typedef struct lx_iocb lx_iocb_t; +typedef struct lx_aiocb lx_aiocb_t; +typedef struct lx_aio_context lx_aio_context_t; + +/* + * Linux binary definition of an I/O event. + */ +struct lx_io_event { + uint64_t lxioe_data; /* data payload */ + uint64_t lxioe_object; /* object of origin */ + int64_t lxioe_res; /* result code */ + int64_t lxioe_res2; /* "secondary" result (WTF?) */ +}; + +/* + * Linux binary definition of an I/O control block. + */ +struct lx_iocb { + uint64_t lxiocb_data; /* data payload */ + uint32_t lxiocb_key; /* must be LX_KIOCB_KEY (!) */ + uint32_t lxiocb_reserved1; + uint16_t lxiocb_op; /* operation */ + int16_t lxiocb_reqprio; /* request priority */ + uint32_t lxiocb_fd; /* file descriptor */ + uint64_t lxiocb_buf; /* data buffer */ + uint64_t lxiocb_nbytes; /* number of bytes */ + int64_t lxiocb_offset; /* offset in file */ + uint64_t lxiocb_reserved2; + uint32_t lxiocb_flags; /* LX_IOCB_FLAG_* flags */ + uint32_t lxiocb_resfd; /* eventfd fd, if any */ +}; + +extern long lx_io_setup(unsigned int, lx_aio_context_t **); +extern long lx_io_submit(lx_aio_context_t *, long nr, uintptr_t **); +extern long lx_io_getevents(lx_aio_context_t *, long, long, + lx_io_event_t *, struct timespec *); +extern long lx_io_cancel(lx_aio_context_t *, lx_iocb_t *, lx_io_event_t *); +extern long lx_io_destroy(lx_aio_context_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_AIO_H */ 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..57692cfcd1 --- /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 (c) 2014 Joyent, Inc. All rights reserved. + */ + +#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(void); + +/* 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..4114d13477 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h @@ -0,0 +1,216 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#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_VERS_MAX]; +extern char lx_cmd_name[MAXNAMLEN]; +extern pid_t zoneinit_pid; + +/* + * Values Linux expects for init + */ +#define LX_INIT_PGID 1 +#define LX_INIT_SID 1 +#define LX_INIT_PID 1 + +/* + * Codes to reboot(2). + */ +#define LINUX_REBOOT_MAGIC1 0xfee1dead +#define LINUX_REBOOT_MAGIC2 672274793 +#define LINUX_REBOOT_MAGIC2A 85072278 +#define LINUX_REBOOT_MAGIC2B 369367448 +#define LINUX_REBOOT_MAGIC2C 537993216 + +/* + * This was observed as coming from Red Hat's init process, but it's not in + * their reboot(2) man page. + */ +#define LINUX_REBOOT_MAGIC2D 0x28121969 + +#define LINUX_REBOOT_CMD_RESTART 0x1234567 +#define LINUX_REBOOT_CMD_HALT 0xcdef0123 +#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc +#define LINUX_REBOOT_CMD_RESTART2 0xa1b2c3d4 +#define LINUX_REBOOT_CMD_CAD_ON 0x89abcdef +#define LINUX_REBOOT_CMD_CAD_OFF 0 + +/* + * 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 + +/* + * Constants to indicate who getrusage() should return information about. + */ +#define LX_RUSAGE_SELF 0 +#define LX_RUSAGE_CHILDREN (-1) +#define LX_RUSAGE_BOTH (-2) +#define LX_RUSAGE_THREAD 1 + +/* + * Constants for prctl(). We only include the ones here that we actually + * support; everything else will be ENOSYS. + */ +#define LX_PR_GET_DUMPABLE 3 +#define LX_PR_SET_DUMPABLE 4 +#define LX_PR_SET_KEEPCAPS 8 +#define LX_PR_SET_NAME 15 + +#define LX_PR_SET_NAME_NAMELEN 16 + +/* + * 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 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); + +extern int lx_check_alloca(size_t); +#define SAFE_ALLOCA(sz) (lx_check_alloca(sz) ? alloca(sz) : NULL) + +extern int ltos_at_flag(int lflag, int allow, boolean_t enforce); + +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); + +/* + * 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..32bfb21ff1 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h @@ -0,0 +1,155 @@ +/* + * 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 _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 +#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_BIND | LX_MS_SILENT | \ + LX_MS_STRICTATIME) + +/* + * 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..64cdb20598 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h @@ -0,0 +1,315 @@ +/* + * 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) + +/* + * NOTE: Linux uses different definitions for sigset_ts and sigaction_ts + * depending on whether the definition is for user space or the kernel. + * + * The definitions below MUST correspond to the Linux kernel versions, + * as glibc will do the necessary translation from the Linux user + * versions. + */ +#if defined(_LP64) +#define LX_NSIG_WORDS 1 +#define LX_WSHIFT 6 +#else /* is _ILP32 */ +#define LX_NSIG_WORDS 2 +#define LX_WSHIFT 5 +#endif + +typedef struct { + ulong_t __bits[LX_NSIG_WORDS]; +} lx_sigset_t; + +#define LX_NBITS (sizeof (ulong_t) * NBBY) +#define lx_sigmask(n) (1UL << (((n) - 1) % LX_NBITS)) +#define lx_sigword(n) (((ulong_t)((n) - 1)) >> LX_WSHIFT) +#define lx_sigismember(s, n) (lx_sigmask(n) & (s)->__bits[lx_sigword(n)]) +#define lx_sigaddset(s, n) ((s)->__bits[lx_sigword(n)] |= lx_sigmask(n)) + +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..5faa795aa4 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h @@ -0,0 +1,436 @@ +/* + * 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 + +/* + * IP Protocol levels. Some of these match the Illumos IPPROTO_* values. + */ +#define LX_IPPROTO_IP 0 +#define LX_IPPROTO_ICMP 1 +#define LX_IPPROTO_IGMP 2 +#define LX_IPPROTO_TCP 6 +#define LX_IPPROTO_UDP 17 +#define LX_IPPROTO_IPV6 41 +#define LX_IPPROTO_ICMPV6 58 +#define LX_IPPROTO_RAW 255 + +/* + * Options for use with [gs]etsockopt at the IP level. + * IPPROTO_IP + */ +#define LX_IP_TOS 1 +#define LX_IP_TTL 2 +#define LX_IP_HDRINCL 3 +#define LX_IP_OPTIONS 4 +#define LX_IP_ROUTER_ALERT 5 +#define LX_IP_RECVOPTS 6 +#define LX_IP_RETOPTS 7 +#define LX_IP_PKTINFO 8 +#define LX_IP_PKTOPTIONS 9 +#define LX_IP_MTU_DISCOVER 10 +#define LX_IP_RECVERR 11 +#define LX_IP_RECVTTL 12 +#define LX_IP_RECVTOS 13 +#define LX_IP_MTU 14 +#define LX_IP_FREEBIND 15 +#define LX_IP_IPSEC_POLICY 16 +#define LX_IP_XFRM_POLICY 17 +#define LX_IP_PASSSEC 18 +#define LX_IP_TRANSPARENT 19 +#define LX_IP_ORIGDSTADDR 20 +#define LX_IP_MINTTL 21 +#define LX_IP_NODEFRAG 22 +/* Linux apparently leaves a gap here */ +#define LX_IP_MULTICAST_IF 32 +#define LX_IP_MULTICAST_TTL 33 +#define LX_IP_MULTICAST_LOOP 34 +#define LX_IP_ADD_MEMBERSHIP 35 +#define LX_IP_DROP_MEMBERSHIP 36 +#define LX_IP_UNBLOCK_SOURC 37 +#define LX_IP_BLOCK_SOURCE 38 +#define LX_IP_ADD_SOURCE_MEMBERSHIP 39 +#define LX_IP_DROP_SOURCE_MEMBERSHIP 40 +#define LX_IP_MSFILTER 41 +#define LX_MCAST_JOIN_GROUP 42 +#define LX_MCAST_BLOCK_SOURCE 43 +#define LX_MCAST_UNBLOCK_SOURCE 44 +#define LX_MCAST_LEAVE_GROUP 45 +#define LX_MCAST_JOIN_SOURCE_GROUP 46 +#define LX_MCAST_LEAVE_SOURCE_GROUP 47 +#define LX_MCAST_MSFILTER 48 +#define LX_IP_MULTICAST_ALL 49 +#define LX_IP_UNICAST_IF 50 + + +/* + * Options for use with [gs]etsockopt at the IP level. + * IPPROTO_IPV6 + */ + +#define LX_IPV6_ADDRFORM 1 +#define LX_IPV6_2292PKTINFO 2 +#define LX_IPV6_2292HOPOPTS 3 +#define LX_IPV6_2292DSTOPTS 4 +#define LX_IPV6_2292RTHDR 5 +#define LX_IPV6_2292PKTOPTIONS 6 +#define LX_IPV6_CHECKSUM 7 +#define LX_IPV6_2292HOPLIMIT 8 +#define LX_IPV6_NEXTHOP 9 +#define LX_IPV6_AUTHHDR 10 +#define LX_IPV6_UNICAST_HOPS 16 +#define LX_IPV6_MULTICAST_IF 17 +#define LX_IPV6_MULTICAST_HOPS 18 +#define LX_IPV6_MULTICAST_LOOP 19 +#define LX_IPV6_JOIN_GROUP 20 +#define LX_IPV6_LEAVE_GROUP 21 +#define LX_IPV6_ROUTER_ALERT 22 +#define LX_IPV6_MTU_DISCOVER 23 +#define LX_IPV6_MTU 24 +#define LX_IPV6_RECVERR 25 +#define LX_IPV6_V6ONLY 26 +#define LX_IPV6_JOIN_ANYCAST 27 +#define LX_IPV6_LEAVE_ANYCAST 28 +#define LX_IPV6_IPSEC_POLICY 34 +#define LX_IPV6_XFRM_POLICY 35 + +#define LX_IPV6_RECVPKTINFO 49 +#define LX_IPV6_PKTINFO 50 +#define LX_IPV6_RECVHOPLIMIT 51 +#define LX_IPV6_HOPLIMIT 52 +#define LX_IPV6_RECVHOPOPTS 53 +#define LX_IPV6_HOPOPTS 54 +#define LX_IPV6_RTHDRDSTOPTS 55 +#define LX_IPV6_RECVRTHDR 56 +#define LX_IPV6_RTHDR 57 +#define LX_IPV6_RECVDSTOPTS 58 +#define LX_IPV6_DSTOPTS 59 +#define LX_IPV6_RECVTCLASS 66 +#define LX_IPV6_TCLASS 67 + +/* + * Options for use with [gs]etsockopt at the IP level. + * IPPROTO_ICMPV6 + */ + +#define LX_ICMP6_FILTER 1 + +/* + * Options for use with [gs]etsockopt at the TCP level. + * IPPROTO_TCP + */ +#define LX_TCP_NODELAY 1 /* Don't delay send to coalesce packets */ +#define LX_TCP_MAXSEG 2 /* Set maximum segment size */ +#define LX_TCP_CORK 3 /* Control sending of partial frames */ +#define LX_TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define LX_TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define LX_TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define LX_TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define LX_TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define LX_TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define LX_TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define LX_TCP_INFO 11 /* Information about this connection. */ +#define LX_TCP_QUICKACK 12 /* Bock/reenable quick ACKs. */ +#define LX_TCP_CONGESTION 13 /* Congestion control algorithm */ +#define LX_TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ +#define LX_TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts on thin streams */ +#define LX_TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ +#define LX_TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ +#define LX_TCP_REPAIR 19 /* TCP socket under repair */ +#define LX_TCP_REPAIR_QUEUE 20 +#define LX_TCP_QUEUE_SEQ 21 +#define LX_TCP_REPAIR_OPTIONS 22 +#define LX_TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ +#define LX_TCP_TIMESTAMP 24 +#define LX_TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes */ + +/* + * Options for use with [gs]etsockopt at the IGMP level. + * IPPROTO_IGMP + */ +#define LX_IGMP_MINLEN 8 +#define LX_IGMP_MAX_HOST_REPORT_DELAY 10 +#define LX_IGMP_HOST_MEMBERSHIP_QUERY 0x11 +#define LX_IGMP_HOST_MEMBERSHIP_REPORT 0x12 +#define LX_IGMP_DVMRP 0x13 +#define LX_IGMP_PIM 0x14 +#define LX_IGMP_TRACE 0x15 +#define LX_IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16 +#define LX_IGMP_HOST_LEAVE_MESSAGE 0x17 +#define LX_IGMP_MTRACE_RESP 0x1e +#define LX_IGMP_MTRACE 0x1f + +/* + * Options for use with [gs]etsockopt at the SOL_SOCKET level. + */ +#define LX_SOL_SOCKET 1 + +#define LX_SCM_RIGHTS 1 +#define LX_SCM_CRED 2 + +#define LX_SO_DEBUG 1 +#define LX_SO_REUSEADDR 2 +#define LX_SO_TYPE 3 +#define LX_SO_ERROR 4 +#define LX_SO_DONTROUTE 5 +#define LX_SO_BROADCAST 6 +#define LX_SO_SNDBUF 7 +#define LX_SO_RCVBUF 8 +#define LX_SO_KEEPALIVE 9 +#define LX_SO_OOBINLINE 10 +#define LX_SO_NO_CHECK 11 +#define LX_SO_PRIORITY 12 +#define LX_SO_LINGER 13 +#define LX_SO_BSDCOMPAT 14 +#define LX_SO_REUSEPORT 15 +/* + * For Linux see unix(7) man page SO_PASSCRED description. For Illumos see + * socket.h(3HEAD) man page SO_RECVUCRED description. + */ +#define LX_SO_PASSCRED 16 +#define LX_SO_PEERCRED 17 +#define LX_SO_RCVLOWAT 18 +#define LX_SO_SNDLOWAT 19 +#define LX_SO_RCVTIMEO 20 +#define LX_SO_SNDTIMEO 21 +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define LX_SO_SECURITY_AUTHENTICATION 22 +#define LX_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define LX_SO_SECURITY_ENCRYPTION_NETWORK 24 +#define LX_SO_BINDTODEVICE 25 +/* Socket filtering */ +#define LX_SO_ATTACH_FILTER 26 +#define LX_SO_DETACH_FILTER 27 +#define LX_SO_PEERNAME 28 +#define LX_SO_TIMESTAMP 29 +#define LX_SCM_TIMESTAMP LX_SO_TIMESTAMP +#define LX_SO_ACCEPTCONN 30 + +#define LX_SO_PEERSEC 31 +#define LX_SO_SNDBUFFORCE 32 +#define LX_SO_RCVBUFFORCE 33 +#define LX_SO_PASSSEC 34 +#define LX_SO_TIMESTAMPNS 35 +#define LX_SCM_TIMESTAMPNS LX_SO_TIMESTAMPNS +#define LX_SO_MARK 36 +#define LX_SO_TIMESTAMPING 37 +#define LX_SCM_TIMESTAMPING LX_SO_TIMESTAMPING +#define LX_SO_PROTOCOL 38 +#define LX_SO_DOMAIN 39 +#define LX_SO_RXQ_OVFL 40 +#define LX_SO_WIFI_STATUS 41 +#define LX_SCM_WIFI_STATUS LX_SO_WIFI_STATUS +#define LX_SO_PEEK_OFF 42 +#define LX_SO_NOFCS 43 +#define LX_SO_LOCK_FILTER 44 +#define LX_SO_SELECT_ERR_QUEUE 45 +#define LX_SO_BUSY_POLL 46 +#define LX_SO_MAX_PACING_RATE 47 +#define LX_SO_BPF_EXTENSIONS 48 + +/* + * Options for use with [gs]etsockopt at the RAW level. + * IPPROTO_RAW + */ +#define LX_ICMP_FILTER 1 + +/* + * Options for use with [gs]etsockopt at the PACKET level. + * SOL_PACKET + */ +#define LX_SOL_PACKET 263 + +#define LX_PACKET_ADD_MEMBERSHIP 1 +#define LX_PACKET_DROP_MEMBERSHIP 2 +#define LX_PACKET_RECV_OUTPUT 3 +#define LX_PACKET_RX_RING 5 +#define LX_PACKET_STATISTICS 6 + +/* + * Options for use with [gs]etsockopt at the NETLINK level. + * SOL_NETLINK + */ +#define LX_SOL_NETLINK 270 +#define LX_SOL_NETLINK_MAX_ENTRY 8 + +/* + * 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 + +/* + * Linux socket flags for use with recv(2)/send(2)/recvmsg(2)/sendmsg(2) + */ +#define LX_MSG_OOB 1 +#define LX_MSG_PEEK 2 +#define LX_MSG_DONTROUTE 4 +#define LX_MSG_CTRUNC 8 +#define LX_MSG_PROXY 0x10 +#define LX_MSG_TRUNC 0x20 +#define LX_MSG_DONTWAIT 0x40 +#define LX_MSG_EOR 0x80 +#define LX_MSG_WAITALL 0x100 +#define LX_MSG_FIN 0x200 +#define LX_MSG_SYN 0x400 +#define LX_MSG_CONFIRM 0x800 +#define LX_MSG_RST 0x1000 +#define LX_MSG_ERRQUEUE 0x2000 +#define LX_MSG_NOSIGNAL 0x4000 +#define LX_MSG_MORE 0x8000 +#define LX_MSG_WAITFORONE 0x10000 +#define LX_MSG_FASTOPEN 0x20000000 +#define LX_MSG_CMSG_CLOEXEC 0x40000000 + +struct lx_msghdr { + void *msg_name; /* optional address */ + socklen_t msg_namelen; /* size of address */ + struct iovec *msg_iov; /* scatter/gather array */ + int msg_iovlen; /* # elements in msg_iov */ + void *msg_control; /* ancillary data */ + socklen_t msg_controllen; /* ancillary data buffer len */ + int msg_flags; /* flags on received message */ +}; + +struct lx_ucred { + pid_t lxu_pid; + lx_uid_t lxu_uid; + lx_gid_t lxu_gid; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_SOCKET_H */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_stat.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_stat.h new file mode 100644 index 0000000000..731555ec74 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_stat.h @@ -0,0 +1,116 @@ +/* + * 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. + */ + +#ifndef _SYS_LX_STAT_H +#define _SYS_LX_STAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/lx_types.h> +#include <sys/stat.h> + +#define LX_MAJORSHIFT 8 +#define LX_MINORMASK ((1 << LX_MAJORSHIFT) - 1) +#define LX_MAKEDEVICE(lx_maj, lx_min) \ + ((lx_dev_t)((lx_maj) << LX_MAJORSHIFT | ((lx_min) & LX_MINORMASK))) + +#define LX_GETMAJOR(lx_dev) ((lx_dev) >> LX_MAJORSHIFT) +#define LX_GETMINOR(lx_dev) ((lx_dev) & LX_MINORMASK) + +#undef st_atime +#undef st_mtime +#undef st_ctime + +struct lx_stat { + lx_dev16_t st_dev; + uint16_t st_pad1; + lx_ino_t st_ino; + lx_mode16_t st_mode; + uint16_t st_nlink; + lx_uid16_t st_uid; + lx_gid16_t st_gid; + lx_dev16_t st_rdev; + uint16_t st_pad2; + lx_off_t st_size; + lx_blksize_t st_blksize; + lx_blkcnt_t st_blocks; + struct lx_timespec st_atime; + struct lx_timespec st_mtime; + struct lx_timespec st_ctime; + uint32_t st_pad3; + uint32_t st_pad4; +}; + +#if defined(_LP64) +struct lx_stat64 { + ulong_t st_dev; + ulong_t st_ino; + ulong_t st_nlink; /* yes, the order really is */ + uint_t st_mode; /* different for these two */ + uint_t st_uid; + uint_t st_gid; + uint_t st_pad0; + ulong_t st_rdev; + long st_size; + long st_blksize; + long st_blocks; + struct lx_timespec st_atime; + struct lx_timespec st_mtime; + struct lx_timespec st_ctime; + long st_unused[3]; +}; + +#else /* is 32-bit */ + +struct lx_stat64 { + lx_dev_t st_dev; + uint32_t st_pad1; + lx_ino_t st_small_ino; + lx_mode_t st_mode; + uint_t st_nlink; + lx_uid_t st_uid; + lx_gid_t st_gid; + lx_dev_t st_rdev; + uint32_t st_pad2; + lx_off64_t st_size; + lx_blksize_t st_blksize; + lx_blkcnt64_t st_blocks; + struct lx_timespec st_atime; + struct lx_timespec st_mtime; + struct lx_timespec st_ctime; + lx_ino64_t st_ino; +}; +#endif + +extern int lx_stat_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LX_STAT_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..72c4c9ebcf --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h @@ -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 2014 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_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_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..ed89a9083b --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h @@ -0,0 +1,340 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +#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_unlinkat(uintptr_t, uintptr_t, uintptr_t); +extern long lx_renameat(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_linkat(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_symlinkat(uintptr_t, uintptr_t, uintptr_t); +extern long lx_readlinkat(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_access(uintptr_t, uintptr_t); +extern long lx_faccessat(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_flock(uintptr_t, uintptr_t); +extern long lx_readlink(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_dup2(uintptr_t, uintptr_t); +extern long lx_dup3(uintptr_t, uintptr_t, uintptr_t); +extern long lx_ioctl(uintptr_t, uintptr_t, uintptr_t); +extern long lx_vhangup(void); +extern long lx_fadvise64(uintptr_t, off64_t, uintptr_t, uintptr_t); +extern long lx_fadvise64_64(uintptr_t, off64_t, off64_t, uintptr_t); + +extern long lx_readv(uintptr_t, uintptr_t, uintptr_t); +extern long lx_writev(uintptr_t, uintptr_t, uintptr_t); +extern long lx_pread(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_pwrite(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_preadv(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_pwritev(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_pread64(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); +extern long lx_pwrite64(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + +extern long lx_socketcall(uintptr_t, uintptr_t); +extern long lx_accept(int, void *, int *); +extern long lx_accept4(int, void *, int *, int); +extern long lx_bind(int, void *, int); +extern long lx_connect(int, void *, int); +extern long lx_getpeername(int, void *, int *); +extern long lx_getsockname(int, void *, int *); +extern long lx_getsockopt(int, int, int, void *, int *); +extern long lx_listen(int, int); +extern long lx_recvfrom(int, void *, size_t, int, void *, socklen_t *); +extern long lx_recvmsg(int, void *, int); +extern long lx_sendmsg(int, void *, int); +extern long lx_sendto(int, void *, size_t, int, void *, int); +extern long lx_setsockopt(int, int, int, void *, int); +extern long lx_shutdown(int, int); +extern long lx_socket(int, int, int); +extern long lx_socketpair(int, int, int, int *); + +extern long lx_select(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_pselect6(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t, uintptr_t); +extern long lx_poll(uintptr_t, uintptr_t, uintptr_t); +extern long lx_ppoll(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_epoll_ctl(uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_settimeofday(uintptr_t, uintptr_t); +extern long lx_getrusage(uintptr_t, uintptr_t); +extern long lx_mknod(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_getpgrp(void); +extern long lx_getpgid(uintptr_t); +extern long lx_setpgid(uintptr_t, uintptr_t); +extern long lx_getsid(uintptr_t); +extern long lx_setsid(void); + +extern long lx_getuid16(void); +extern long lx_getgid16(void); +extern long lx_geteuid16(void); +extern long lx_getegid16(void); +extern long lx_geteuid(void); +extern long lx_getegid(void); +extern long lx_getresuid16(uintptr_t, uintptr_t, uintptr_t); +extern long lx_getresgid16(uintptr_t, uintptr_t, uintptr_t); +extern long lx_getresuid(uintptr_t, uintptr_t, uintptr_t); +extern long lx_getresgid(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_setuid16(uintptr_t); +extern long lx_setreuid16(uintptr_t, uintptr_t); +extern long lx_setregid16(uintptr_t, uintptr_t); +extern long lx_setgid16(uintptr_t); +extern long lx_setfsuid16(uintptr_t); +extern long lx_setfsgid16(uintptr_t); + +extern long lx_setfsuid(uintptr_t); +extern long lx_setfsgid(uintptr_t); + +extern long lx_clock_nanosleep(int, int flags, struct timespec *, + struct timespec *); +extern long lx_adjtimex(void *); +extern long lx_timer_create(int, struct sigevent *, timer_t *); +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_link(uintptr_t, uintptr_t); +extern long lx_unlink(uintptr_t); +extern long lx_rmdir(uintptr_t); +extern long lx_rename(uintptr_t, uintptr_t); +extern long lx_utime(uintptr_t, uintptr_t); +extern long lx_llseek(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_lseek(uintptr_t, uintptr_t, uintptr_t); +extern long lx_sysfs(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_getcwd(uintptr_t, uintptr_t); +extern long lx_uname(uintptr_t); +extern long lx_reboot(uintptr_t, uintptr_t, uintptr_t, 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_personality(uintptr_t); + +extern long lx_query_module(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t); + +extern long lx_times(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_mlock(uintptr_t, uintptr_t); +extern long lx_mlockall(uintptr_t); +extern long lx_munlock(uintptr_t, uintptr_t); +extern long lx_munlockall(void); +extern long lx_msync(uintptr_t, uintptr_t, uintptr_t); +extern long lx_madvise(uintptr_t, uintptr_t, uintptr_t); +extern long lx_mprotect(uintptr_t, uintptr_t, uintptr_t); +extern long lx_mmap(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t, uintptr_t); +extern long lx_mmap2(uintptr_t, uintptr_t, uintptr_t, uintptr_t, + uintptr_t, uintptr_t); +extern long lx_remap(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_mount(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern long lx_umount(uintptr_t); +extern long lx_umount2(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_sync(void); + +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_sethostname(uintptr_t, uintptr_t); +extern long lx_setdomainname(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_getpriority(uintptr_t, uintptr_t); +extern long lx_setpriority(uintptr_t, uintptr_t, uintptr_t); + +extern long lx_ptrace(uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_sched_getaffinity(uintptr_t, uintptr_t, uintptr_t); +extern long lx_sched_setaffinity(uintptr_t, uintptr_t, uintptr_t); +extern long lx_sched_getparam(uintptr_t, uintptr_t); +extern long lx_sched_setparam(uintptr_t, uintptr_t); +extern long lx_sched_rr_get_interval(uintptr_t pid, uintptr_t); +extern long lx_sched_getscheduler(uintptr_t); +extern long lx_sched_setscheduler(uintptr_t, uintptr_t, uintptr_t); +extern long lx_sched_get_priority_min(uintptr_t); +extern long lx_sched_get_priority_max(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_prctl(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t); + +extern long lx_alarm(unsigned int); +extern long lx_close(int); +extern long lx_chdir(const char *); +extern long lx_chroot(const char *); +extern long lx_creat(const char *, mode_t); +extern long lx_dup(int); +extern long lx_epoll_pwait(int, void *, int, int, const sigset_t *); +extern long lx_epoll_create(int); +extern long lx_epoll_create1(int); +extern long lx_epoll_wait(int, void *, int, int); +extern long lx_eventfd(unsigned int); +extern long lx_eventfd2(unsigned int, int); +extern long lx_fchdir(int); +extern long lx_getgid(void); +extern long lx_getgroups(int, gid_t *); +extern long lx_getitimer(int, struct itimerval *); +extern long lx_getuid(void); +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_mincore(caddr_t, size_t, char *); +extern long lx_munmap(void *, size_t); +extern long lx_nanosleep(const struct timespec *, struct timespec *); +extern long lx_nice(int); +extern long lx_pause(void); +extern long lx_setgid(gid_t); +extern long lx_setuid(uid_t); +extern long lx_setregid(gid_t, gid_t); +extern long lx_setreuid(uid_t, uid_t); +extern long lx_shmdt(char *); +extern long lx_stime(const time_t *); +extern long lx_symlink(const char *, const char *); +extern long lx_syslog(int, char *, 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_umask(mode_t); +extern long lx_utimes(const char *, const struct timeval *); + +#endif /* !defined(_ASM) */ + + +#if defined(_LP64) +#define LX_SYS_clone 56 +#else +#define LX_SYS_clone 120 +#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..3867f621f5 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h @@ -0,0 +1,78 @@ +/* + * 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 + */ + +#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 { + 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..81087058f7 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/Makefile @@ -0,0 +1,53 @@ +# +# 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. +# + +PROG = lxinit +PROGS = $(PROG) +OBJS = lxinit + +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 -lipadm -lsocket -linetutil -lnsl + +.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_init/lxinit.c b/usr/src/lib/brand/lx/lx_init/lxinit.c new file mode 100644 index 0000000000..f7f27bf9b6 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_init/lxinit.c @@ -0,0 +1,549 @@ +/* + * 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. + */ + +/* + * lxinit performs zone-specific initialization prior to handing control to the + * guest Linux init. This primarily consists of: + * + * - Starting ipmgmtd chrooted into /native + * - Configuring network interfaces + * - Adding a default route + */ + +#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/sockio.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/varargs.h> +#include <unistd.h> +#include <libintl.h> +#include <locale.h> + +#include <arpa/inet.h> +#include <net/route.h> +#include <libipadm.h> +#include <libzonecfg.h> +#include <libinetutil.h> +#include <sys/lx_brand.h> + +static void lxi_err(char *msg, ...) __NORETURN; +static void lxi_err(char *msg, ...); + +#define CHROOT_NAME "lx_init-chroot" +#define CHROOT_PREFIX "/native" + +#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_net_ipmgmtd_start() +{ + pid_t pid; + int status; + char *cmd[] = { + "/lib/inet/ipmgmtd", + "ipmgmtd", + NULL + }; + char *env[] = { + /* ipmgmtd thinks SMF is awesome */ + "SMF_FMRI=svc:/network/ip-interface-management:default", + NULL + }; + + pid = fork(); + if (pid == -1) { + lxi_err("fork() failed: %s", strerror(errno)); + } + + if (pid == 0) { + /* child */ + execve(cmd[0], cmd + 1, env); + + lxi_err("execve(%s) failed: %s", cmd[0], + strerror(errno)); + /* NOTREACHED */ + } + + /* parent */ + while (wait(&status) != pid) { + /* EMPTY */; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + lxi_err("ipmgmtd[%d] exited: %d", + (int)pid, WEXITSTATUS(status)); + } + } else if (WIFSIGNALED(status)) { + lxi_err("ipmgmtd[%d] died on signal: %d", + (int)pid, WTERMSIG(status)); + } else { + lxi_err("ipmgmtd[%d] failed in unknown way", + (int)pid); + } +} + +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_iface_ipv4(const char *iface, const char *addr, const char *netmask) +{ + ipadm_status_t status; + ipadm_addrobj_t ipaddr; + char cidraddr[BUFSIZ]; + int prefixlen; + struct sockaddr_in mask_sin; + + mask_sin.sin_family = AF_INET; + if (inet_pton(AF_INET, netmask, &mask_sin.sin_addr) != 1) { + lxi_warn("invalid netmask address: %s\n", strerror(errno)); + return (-1); + } + + prefixlen = mask2plen((struct sockaddr *)&mask_sin); + (void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d", + addr, prefixlen); + + if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, iface, &ipaddr)) + != IPADM_SUCCESS) { + lxi_warn("ipadm_create_addrobj error %d: addr %s (%s), " + "interface %s: %s\n", status, addr, cidraddr, iface, + ipadm_status2str(status)); + return (-2); + } + + if ((status = ipadm_set_addr(ipaddr, cidraddr, AF_INET)) + != IPADM_SUCCESS) { + lxi_warn("ipadm_set_addr error %d: addr %s (%s)" + ", interface %s: %s\n", status, addr, cidraddr, + iface, ipadm_status2str(status)); + return (-3); + } + + 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)); + ipadm_destroy_addrobj(ipaddr); + return (-4); + } + + ipadm_destroy_addrobj(ipaddr); + return (0); +} + +static int +lxi_iface_ipv6(const char *iface) +{ + struct lifreq lifr; + int s; + + /* XXX: Just perform link-local init for now */ + 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 *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 */ + dst_sin->sin_family = AF_INET; + netmask_sin->sin_family = AF_INET; + + dst_sin->sin_family = AF_INET; + if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) { + lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno)); + return (-1); + } + + 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"; + + lxi_net_plumb(iface); + (void) lxi_iface_ipv4(iface, "127.0.0.1", "255.0.0.0"); + (void) lxi_iface_ipv6(iface); +} + +static void +lxi_net_setup(zone_dochandle_t handle) +{ + struct zone_nwiftab lookup; + + 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 *ipaddr, *netmask, *primary, *gateway; + + lxi_net_plumb(iface); + if (zone_find_attr(attrs, "ip", &ipaddr) != 0) { + continue; + } + if (zone_find_attr(attrs, "netmask", &netmask) != 0) { + lxi_err("could not find netmask for interface"); + /* NOTREACHED */ + } + if (lxi_iface_ipv4(iface, ipaddr, netmask) != 0 || + lxi_iface_ipv6(iface) != 0) { + continue; + } + if (zone_find_attr(attrs, "primary", &primary) == 0 && + strncmp(primary, "true", MAXNAMELEN) == 0 && + zone_find_attr(attrs, "gateway", &gateway) == 0) { + lxi_iface_gateway(iface, gateway); + } + } + (void) zonecfg_endnwifent(handle); +} + +static void +lxi_config_close(zone_dochandle_t handle) +{ + zonecfg_fini_handle(handle); +} + +static void +lxi_init_exec() +{ + char *cmd[] = {"/sbin/init", "init", NULL}; + char *env[] = {"container=zone", NULL}; + + /* + * 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[0], cmd + 1, env); + + /* + * 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("exec(%s) failed: %s", cmd[0], strerror(errno)); +} + +int +main(int argc, char *argv[]) +{ + zone_dochandle_t handle; + pid_t pid; + int status = 0; + + if (strcmp(argv[0], CHROOT_NAME) == 0) { + /* we've been forked/chrooted, run setup */ + 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_ipadm_close(); + /* failures will bail before now */ + exit(0); + } + + lxi_log_open(); + + /* The config stuff needs to be chrooted to /native */ + pid = fork(); + if (pid < 0) { + lxi_err("fork() failed: %s"), strerror(errno); + } else if (pid == 0) { + const char *execname; + char *args[] = { + CHROOT_NAME, NULL + }; + + if ((execname = getexecname()) == NULL) { + lxi_err("getexecname() failed: %s", + strerror(errno)); + } + execname += strlen(CHROOT_PREFIX); + + if (chroot(CHROOT_PREFIX) != 0) { + lxi_err("chroot() failed: %s", + strerror(errno)); + } + execv(execname, args); + exit(1); + } else { + /* parent */ + while (wait(&status) != pid) { + /* EMPTY */; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + lxi_err("configuration error %d", status); + } + } + + lxi_log_close(); + lxi_init_exec(); + /* NOTREACHED */ + return (0); +} 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..e7c958e13a --- /dev/null +++ b/usr/src/lib/brand/lx/lx_support/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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +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 + +.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..d1f96defb5 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_support/lx_support.c @@ -0,0 +1,362 @@ +/* + * 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 2015 Joyent, Inc. All rights reserved. + */ + +/* + * 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" + +#define KVSTRLEN 10 + +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*/ +} + +/* + * The Linux init(1M) command requires communication over the /dev/initctl + * FIFO. Since any attempt to create a file in /dev will fail, we must + * create it here. + */ +static void +lxs_make_initctl() +{ + char cmdbuf[ARG_MAX]; + char path[MAXPATHLEN]; + char special[MAXPATHLEN]; + struct stat buf; + int err; + + if (snprintf(special, sizeof (special), "%s/dev/initctl", zoneroot) >= + sizeof (special)) + lxs_err("%s: %s", gettext("Failed to create /dev/initctl"), + gettext("zoneroot is too long")); + + if (snprintf(path, sizeof (path), "%s/root/dev/initctl", zoneroot) >= + sizeof (path)) + lxs_err("%s: %s", gettext("Failed to create /dev/initctl"), + gettext("zoneroot is too long")); + + /* create the actual fifo as <zoneroot>/dev/initctl */ + if (stat(special, &buf) != 0) { + err = errno; + if (err != ENOENT) + lxs_err("%s: %s", + gettext("Failed to create /dev/initctl"), + strerror(err)); + if (mkfifo(special, 0644) < 0) { + err = errno; + lxs_err("%s: %s", + gettext("Failed to create /dev/initctl"), + strerror(err)); + } + } else { + if ((buf.st_mode & S_IFIFO) == 0) + lxs_err("%s: %s", + gettext("Failed to create /dev/initctl"), + gettext("It already exists, and is not a FIFO.")); + } + + /* + * now lofs mount the <zoneroot>/dev/initctl fifo onto + * <zoneroot>/root/dev/initctl + */ + if (snprintf(cmdbuf, sizeof (cmdbuf), "%s -F lofs %s %s", MOUNT_CMD, + special, path) >= sizeof (cmdbuf)) + lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"), + gettext("zoneroot is too long")); + + if (system(cmdbuf) < 0) { + err = errno; + lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"), + strerror(err)); + } +} + +/* + * 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 **kvers) +{ + 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")); + } + + *kvers = (char *)malloc(KVSTRLEN); + if (*kvers == NULL) + lxs_err(gettext("out of memory")); + + bzero(*kvers, KVSTRLEN); + while ((err = zonecfg_getattrent(zdh, &attrtab)) == Z_OK) { + if ((strcmp(attrtab.zone_attr_name, "kernel-version") == 0) && + (zonecfg_get_attr_string(&attrtab, *kvers, + KVSTRLEN) != Z_OK)) + lxs_err(gettext("invalid type for zone attribute: %s"), + attrtab.zone_attr_name); + } + + if (strlen(*kvers) == 0) { + free(*kvers); + *kvers = NULL; + } + + /* some kind of error while looking up attributes */ + if (err != Z_NO_ENTRY) + lxs_err(gettext("error accessing zone configuration")); +} + +static int +lxs_boot() +{ + zoneid_t zoneid; + zone_dochandle_t zdh; + char *kvers; + + lxs_make_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, &kvers); + 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 (kvers != NULL) { + /* Backward compatability with incomplete version attr */ + if (strcmp(kvers, "2.4") == 0) { + kvers = "2.4.21"; + } else if (strcmp(kvers, "2.6") == 0) { + kvers = "2.6.18"; + } + + if (zone_setattr(zoneid, LX_KERN_VERSION_NUM, kvers, + strlen(kvers)) < 0) + lxs_err(gettext("unable to set kernel version")); + } + + return (0); +} + +static int +lxs_halt() +{ + return (0); +} + +static int +lxs_verify(char *xmlfile) +{ + zone_dochandle_t handle; + char *kvers; + 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, &kvers); + zonecfg_fini_handle(handle); + + if (kvers) { + if (strlen(kvers) > (LX_VERS_MAX - 1) || + (strncmp(kvers, "2.4", 3) != 0 && + strncmp(kvers, "2.6", 3) != 0 && + strncmp(kvers, "3.", 2) != 0)) + lxs_err(gettext("invalid value for zone attribute: %s"), + "kernel-version"); + } + 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..b71a8a1aeb --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/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 2015 Joyent, Inc. +# + +include ../../../Makefile.lib + +SUBDIRS = tools +$(BUILD64)SUBDIRS += $(MACH64) + +LINT_SUBDIRS = +$(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) + +$(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..0a957790d3 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/Makefile.com @@ -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 2015 Joyent, Inc. +# + +LIBRARY = lx_vdso.a +VERS = .1 + +COBJS = lx_vdso.o +OBJECTS = $(COBJS) + +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 = lx_vdso.o +OBJECTS = $(ASOBJS) + +ASSRCS = $(ASOBJS:%o=$(ISASRCDIR)/%s) +SRCS = $(ASSRCS) + +SRCDIR = ../common +UTSBASE = ../../../../../uts + +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 + +pics/%.o: $(ISASRCDIR)/%.s + $(COMPILE.s) -o $@ $< + $(POST_PROCESS_O) 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..cdf6eaa62d --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/amd64/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 2014 Joyent, Inc. All rights reserved. +# + +ISASRCDIR=. + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +# +# 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/lx_vdso.s b/usr/src/lib/brand/lx/lx_vdso/amd64/lx_vdso.s new file mode 100644 index 0000000000..039f9b95b3 --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/amd64/lx_vdso.s @@ -0,0 +1,132 @@ +/* + * + * 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 <sys/asm_linkage.h> +#include <sys/trap.h> + +/* + * lx vDSO emulation library + * + * This so needs to look like the correct Linux vDSO elf library. We cannot + * use any native symbols or link with any native libraries, particularly libc. + */ + +#define LX_SYS_gettimeofday 96 +#define LX_SYS_time 201 +#define LX_SYS_clock_gettime 228 +#define LX_SYS_getcpu 309 + +#if defined(lint) +int +__vdso_gettimeofday(void *tp, void *tz) +{} + +time_t +__vdso_time(void *tp) +{} + +time_t +__vdso_clock_gettime(uintptr_t id, void *tp) +{} + +int +__vdso_getcpu(void *cpu, void *np, void *cp) +{} + +#else /* lint */ + + /* + * We know the arguments are already in the correct registers (e.g. arg0 + * already in %rdi, arg1 already in %rsi, etc.). %rax has result of + * call. + */ + + /* + * Uses fasttrap, based on lib/libc/amd64/sys/gettimeofday.s + */ + ENTRY_NP(__vdso_gettimeofday) + pushq %rdi /* pointer to timeval */ + movl $T_GETHRESTIME, %eax + int $T_FASTTRAP + /* + * gethrestime trap returns seconds in %rax, nsecs in %edx + * need to convert nsecs to usecs & store into area pointed + * to by struct timeval * argument. + */ + popq %rcx /* pointer to timeval */ + jrcxz 1f /* bail if we get a null pointer */ + movq %rax, (%rcx) /* store seconds into timeval ptr */ + movl $274877907, %eax /* divide by 1000 as impl. by gcc */ + imull %edx /* See Hacker's Delight pg 162 */ + sarl $6, %edx /* simplified by 0 <= nsec <= 1e9 */ + movq %rdx, 8(%rcx) /* store usecs into timeval ptr + 8. */ +1: + xorq %rax, %rax /* return 0 */ + ret + SET_SIZE(__vdso_gettimeofday) + + /* + * Uses fasttrap, based on lib/libc/amd64/sys/gettimeofday.s, but only + * returns seconds. This is based on what the kernel's gtime function + * will do. + */ + ENTRY_NP(__vdso_time) + pushq %rdi /* pointer to time_t */ + movl $T_GETHRESTIME, %eax + int $T_FASTTRAP + /* + * gethrestime trap returns seconds in %rax + * store secs into area pointed by time_t * argument. + */ + popq %rcx /* pointer to time_t */ + jrcxz 1f /* don't save if we get a null pointer */ + movq %rax, (%rcx) /* store seconds into time_t ptr */ +1: + ret /* return seconds in %rax */ + SET_SIZE(__vdso_time) + + /* + * Does not use fasttrap since there more work to emulate than we can + * do with a fasttrap. + */ + ENTRY_NP(__vdso_clock_gettime) + movq $LX_SYS_clock_gettime, %rax + syscall + ret + SET_SIZE(__vdso_clock_gettime) + + /* + * Uses fasttrap. + * getcpu takes 3 pointers but we only support saving the cpu ID into + * the first pointer. + */ + ENTRY_NP(__vdso_getcpu) + pushq %rdi /* pointer to int */ + movl $T_GETLGRP, %eax + int $T_FASTTRAP + /* + * getlgrp trap returns CPU ID in %eax + * store it into area pointed by int * argument. + */ + popq %rcx /* pointer to int */ + jrcxz 1f /* don't save if we get a null pointer */ + movl %eax, (%rcx) /* store CPU ID into int ptr */ +1: + xorq %rax, %rax /* return 0 */ + ret + SET_SIZE(__vdso_getcpu) +#endif 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/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..0e1d99f9da --- /dev/null +++ b/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c @@ -0,0 +1,388 @@ +/* + * 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. + */ + +/* + * 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) == + NULL) { + (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..ce5c2a47ff --- /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 2015 Joyent, Inc. All rights reserved. +# + +TXTS = ltp_skiplist ltp_tests + +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..d29be61e7a --- /dev/null +++ b/usr/src/lib/brand/lx/testing/Readme_ltp @@ -0,0 +1,309 @@ +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 + +As a normal user: + git clone https://github.com/linux-test-project/ltp.git + cd ltp + 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 + +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 +# eventfd01 +# eventfd2_01 +# eventfd2_02 +# eventfd2_03 +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 +# 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 +# get_robust_list01 OS-3224 +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 +# inotify_init1_01 +# inotify_init1_02 +# inotify01 +# inotify02 +# inotify03 +# inotify04 +# inotify05 +# 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 +# prctl01 get/set deathsig +# prctl02 more deathsig +# 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 +# set_robust_list01 OS-3224 +x settimeofday01 +# setxattr01 +# setxattr02 +# setxattr03 +# shmget05 OS-3326 +# sighold02 fails on sig 63 OS-3307 +# signalfd01 +# signalfd4_01 +# signalfd4_02 +# sigrelse01 fails on sig 63 OS-3307 +# 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 +# timerfd01 +# timerfd02 +# timerfd03 +# timerfd_create01 +# timerfd_gettime01 +# timerfd_settime01 +# 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..0755aace85 --- /dev/null +++ b/usr/src/lib/brand/lx/testing/ltp_skiplist @@ -0,0 +1,265 @@ +# +# 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. + +# 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 +mlockall02 +mlockall03 +mlock02 +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 +mremap01 +mremap02 +mremap03 +mremap04 +mremap05 +open01 +open11 +ptrace04 +quotactl01 +quotactl02 +remap_file_pages01 +remap_file_pages02 +sched_getparam01 +sched_getparam02 +sched_setscheduler01 +sched_setaffinity01 +sched_getaffinity01 +sched_setparam02 +sched_setparam03 +setfsuid04 +setfsuid04_16 +settimeofday01 +stime01 +swapoff01 +swapoff02 +swapon01 +swapon02 +swapon03 +switch01 +ustat01 +ustat02 + +# needs a config with a dev or mounting +chmod06 +chown04 +chown04_16 +creat06 +fchmod06 +fchown04 +fchown04_16 +inotify03 +lchown03 +lchown03_16 +linkat02 +link08 +mkdir03 +mkdirat02 +mknod07 +mknodat02 +mount01 +mount02 +mount04 +rename11 +renameat01 +rmdir02 +umount01 +umount02 +umount03 +utime06 +utimes01 + +# tests functionality not implemented yet +access06 +add_key01 +add_key02 +clone02 +clone08 +crash02 +eventfd01 +eventfd2_01 +eventfd2_02 +eventfd2_03 +fallocate01 +fallocate01 +fallocate01 +fanotify01 +fanotify02 +fanotify03 +fanotify04 +fanotify05 +fcntl06 +fcntl23 +fcntl23_64 +fcntl24 +fcntl24_64 +fcntl25 +fcntl25_64 +fcntl26 +fcntl26_64 +fcntl30 +fcntl30_64 +fcntl31 +fcntl31_64 +fork05 +fork09 +fork13 +fork14 +ftruncate04 +ftruncate04_64 +getdents02 +getdents02_64 +get_mempolicy01 +get_robust_list01 +ioctl03 +io_cancel01 +io_destroy01 +io_getevents01 +io_setup01 +io_submit01 +keyctl01 +mmap13 +mq_notify01 +mq_notify02 +mq_open01 +mq_timedreceive01 +mq_timedsend01 +mq_unlink01 +msgctl12 +msgrcv07 +open02 +open10 +perf_event_open01 +perf_event_open02 +ppoll01 +prctl01 +prctl02 +process_vm_readv01 +process_vm_readv02 +process_vm_readv03 +process_vm_writev01 +process_vm_writev02 +prot_hsymlinks +read02 +readahead01 +readahead02 +readdir2 +readdir21 +sched_rr_get_interval01 +sched_rr_get_interval02 +sched_rr_get_interval03 +sched_getscheduler01 +sched_getscheduler02 +semctl01 +semop02 +sendfile02 # OS-3296 +sendfile02_64 +sendfile04 +sendfile04_64 +sendfile06 +sendfile06_64 +sendfile07 +sendfile07_64 +sendmsg01 +setgroups04_16 +setns01 +setns02 +setpgid02 +setregid02 +setregid02_16 +setrlimit01 +set_robust_list01 # OS-3224 +setxattr01 +setxattr02 +setxattr03 +sgetmask01 +shmget05 # OS-3326 +signalfd01 +signalfd4_01 +signalfd4_02 +splice01 +splice02 +splice03 +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 +timerfd01 +timerfd02 +timerfd03 +timerfd_create01 +timerfd_gettime01 +timerfd_settime01 +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/zone/Makefile b/usr/src/lib/brand/lx/zone/Makefile new file mode 100644 index 0000000000..4d681a1b45 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/Makefile @@ -0,0 +1,70 @@ +# +# 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 2015 Joyent, Inc. All rights reserved. +# + +PROGS = lx_boot +PROGS += lx_boot_zone_redhat lx_boot_zone_ubuntu lx_boot_zone_debian +PROGS += lx_boot_zone_busybox +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..78130750e3 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/config.xml @@ -0,0 +1,105 @@ +<?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 %R</install> + <installopts>d:hsvX</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> + + <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_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_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_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..9f4746e20a --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot.ksh @@ -0,0 +1,92 @@ +#!/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 2015, 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" +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..7580e9022a --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh @@ -0,0 +1,155 @@ +#!/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 +$(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") + } + } +') + 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..b97026a8ab --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh @@ -0,0 +1,164 @@ +#!/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 +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_redhat.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh new file mode 100644 index 0000000000..f3ee519429 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh @@ -0,0 +1,330 @@ +#!/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 + echo "# AUTOMATIC ZONE CONFIG" > /etc/resolv.conf +$(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") + } + } +') + 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 + + +# +# 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_ubuntu.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh new file mode 100644 index 0000000000..0674e77158 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh @@ -0,0 +1,137 @@ +#!/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 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 +fnm=$ZONEROOT/etc/resolvconf/resolv.conf.d/tail +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") +} +$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 + +# udev-bridge currently aborts and drops core due to missing AF_NETLINK support +fnm=$ZONEROOT/etc/init/upstart-udev-bridge.override +if [[ ! -f $fnm && ! -h $fnm ]] then + echo "manual" > $fnm +fi + +# XXX need to add real mounting into this svc definition + +fnm=$ZONEROOT/etc/init/mountall.override +if [[ ! -f $fnm && ! -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 "/ / 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_install.ksh b/usr/src/lib/brand/lx/zone/lx_install.ksh new file mode 100644 index 0000000000..c51c4fa008 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/lx_install.ksh @@ -0,0 +1,188 @@ +#!/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. All rights reserved. +# + +# +# This is only an example install script. It is not currently used for anything. +# + +PATH=/bin:/usr/bin:/usr/sbin +export PATH + +fullpath() +{ + typeset path="$1" + + echo $path | egrep -s "^/" || path="${PWD:=$(pwd)}/$path" + echo $path +} + +makedir() +{ + typeset dirname=$(fullpath "$1") + typeset mode="" + + [[ $# -eq 2 ]] && mode="-m $2" + + [[ -d "$dirname" ]] && return + + if ! mkdir $mode -p "$dirname"; then + echo $(gettext "Aborting installation...") + exit 255 + fi +} + +symlink() +{ + typeset src="$1" + typeset dst=$(fullpath "$2") + + [[ -e "$dst" || -h "$dst" ]] && rm -f "$dst" + + if ! ln -s "$src" "$dst"; then + echo $(gettext "Aborting installation...") + exit 255 + fi +} + +install_ln() +{ + typeset source="$1" + typeset target=$(fullpath "$2") + + log " Installing \"$target\"" + + mv -f "$target" "$target.$tag" 2>/dev/null + + if ! ln -s "$source" "$target"; then + return 1 + fi + + return 0 +} + +# If we weren't passed 3 arguments, exit now. +[[ $# -lt 3 ]] && exit 254 + +# Extract the brand directory name from the path. +branddir=$(dirname "$0") +zonename="$1" +zoneroot="$2" +install_src="3" +install_root="$zoneroot/root" + +if [[ ! -f "$install_src" ]]; then + echo "$install_src: file not found\n" + exit 254 +fi + +if [[ ! -d "$install_root" ]]; then + if ! mkdir -p "$install_root" 2>/dev/null; then + echo "Could not create install directory $install_root" + exit 254 + fi +fi + +if ! ( cd "$install_root" && gtar -xzf "$install_src" ) ; then + echo "Error: extraction from tar archive failed" + exit 255 +fi + +tag="lxsave_$(date +%m.%d.%Y@%T)" + +if [[ ! -d "$install_root" ]]; then + exit 255 +fi + +cd "$install_root" + +makedir native/dev +makedir native/etc/default +makedir native/etc/svc/volatile +makedir native/lib +makedir native/proc +makedir native/tmp 1777 +makedir native/usr +makedir native/var + +makedir mnt +makedir opt +makedir usr/local/bin +makedir usr/local/include +makedir usr/local/lib +makedir usr/local/sbin +makedir usr/local/share +makedir usr/local/src + +makedir dev 0755 +makedir tmp 1777 +makedir proc 0555 +makedir boot 0755 + +symlink /bin/sh sbin/sh +symlink /bin/su usr/bin/su +symlink /native/usr/lib/ld.so.1 usr/lib/ld.so.1 + +libpam_so="$(echo lib/libpam.so.0.*)" +libpam_misc="$(echo lib/libpam_misc.so.0.*)" +libpamc_so="$(echo lib/libpamc.so.0.*)" + +symlink "/$libpam_so" lib/libpam.so.0 +symlink "/$libpam_misc" lib/libpam_misc.so.0 +symlink "/$libpamc_so" lib/libpamc.so.0 + +makedir var/ld + +if ! crle -c var/ld/ld.config -l /native/lib:/native/usr/lib \ + -s /native/lib/secure:/native/usr/lib/secure; then + exit 255 +fi + +mv -f etc/fstab etc/fstab.$tag 2>/dev/null + +cat > etc/fstab <<- EOF + none / zfs defaults 1 1 + proc /proc proc defaults 0 0 +EOF + +if [[ $? -ne 0 ]]; then + exit 255 +fi + +if [[ ! -e "$install_root/etc/hosts" ]]; then + cat > "$install_root/etc/hosts" <<-_EOF_ + 127.0.0.1 localhost + _EOF_ +fi + +# +# Perform distribution-specific changes. +# +distro="" +if [[ -f etc/redhat-release ]]; then + distro="redhat" +elif [[ -f etc/lsb-release ]]; then + if egrep -s Ubuntu etc/lsb-release; then + distro="ubuntu" + elif [[ -f etc/debian_version ]]; then + distro="debian" + fi +elif [[ -f etc/debian_version ]]; then + distro="debian" +fi + +if [[ -z $distro ]]; then + exit 255 +fi + +exit 0 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..f841795530 --- /dev/null +++ b/usr/src/lib/brand/lx/zone/platform.xml @@ -0,0 +1,155 @@ +<?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 2015 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/netconfig" + directory="/native/etc/netconfig" 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" /> + + <!-- Local filesystems to mount when booting the zone --> + <mount special="/native/dev" directory="/dev" type="lofs" /> + <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="inotify" /> + <device match="ipnet" /> + <device match="kstat" /> + <device match="lo0" /> + <device match="null" /> + <device match="poll" /> + <device match="pts/*" /> + <device match="random" /> + <device match="tcp" /> + <device match="tcp6" /> + <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/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" /> + + <!-- 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="log" /> + + <!-- Create a mount point for the /dev/initctl fifo --> + <device match="null" name="initctl" /> + <!-- Create a dummy for the /dev/log socket --> + <device match="null" name="log" /> + <!-- 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/shared/zone/common.ksh b/usr/src/lib/brand/shared/zone/common.ksh index 52441d814a..0f87686414 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/brand/sngl/Makefile b/usr/src/lib/brand/sngl/Makefile new file mode 100644 index 0000000000..780acc21e1 --- /dev/null +++ b/usr/src/lib/brand/sngl/Makefile @@ -0,0 +1,51 @@ +# +# 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. +# + +default: all + +include Makefile.sngl + +# Build everything in parallel; use .WAIT for dependencies +.PARALLEL: + +SUBDIRS = sngl_brand zone +MSGSUBDIRS = 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/sngl/Makefile.sngl b/usr/src/lib/brand/sngl/Makefile.sngl new file mode 100644 index 0000000000..0c7a280696 --- /dev/null +++ b/usr/src/lib/brand/sngl/Makefile.sngl @@ -0,0 +1,28 @@ +# +# 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. +# + +BRAND = sngl + +include $(SRC)/lib/brand/Makefile.brand + diff --git a/usr/src/lib/brand/sngl/sngl_brand/Makefile b/usr/src/lib/brand/sngl/sngl_brand/Makefile new file mode 100644 index 0000000000..4b667e1fa8 --- /dev/null +++ b/usr/src/lib/brand/sngl/sngl_brand/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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include $(SRC)/lib/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/sngl/sngl_brand/Makefile.com b/usr/src/lib/brand/sngl/sngl_brand/Makefile.com new file mode 100644 index 0000000000..070a03dcb5 --- /dev/null +++ b/usr/src/lib/brand/sngl/sngl_brand/Makefile.com @@ -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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, Joyent, Inc. All rights reserved. +# + +LIBRARY = sngl_brand.a +VERS = .1 +COBJS = sngl_brand.o +ASOBJS = crt.o handler.o runexe.o brand_util.o +OBJECTS = $(COBJS) + +include ../../Makefile.sngl +include $(SRC)/lib/Makefile.lib + +SRCDIR = ../common +UTSBASE = $(SRC)/uts + +LIBS = $(DYNLIB) +CSRCS = $(COBJS:%o=../common/%c) +SHAREDOBJS = $(ASOBJS:%o=$(ISAOBJDIR)/%o) +SRCS = $(CSRCS) + +# +# Note that the architecture specific makefiles MUST update DYNFLAGS to +# explicitly specify an interpreter for the brand emulation library so that we +# use /lib/ld.so.1 or /lib/64/ld.so.1, which in a sngl zone is the system +# linker. +# +# Note that since the linker is used to setup processes before libc is loaded, +# it makes system calls directly (ie avoiding libc), and it makes these system +# calls before our library has been initialized. Since our library hasn't been +# initialized yet, there's no way for us to intercept and emulate any of those +# system calls. Luckily we don't have to. +# +# Note that we make sure to link our brand emulation library to libmapmalloc. +# This is required because in most cases there will be two copies of libc in +# the same process and we don't want them to fight over the heap. So for our +# brand library we link against libmapmalloc so that if we (our or copy of +# libc) try to allocate any memory it will be done via mmap() instead of brk(). +# +CPPFLAGS += -D_REENTRANT -U_ASM \ + -I. -I$(BRAND_SHARED)/brand/sys -I$(UTSBASE)/common/brand/sngl +CFLAGS += $(CCVERBOSE) +DYNFLAGS += $(DYNFLAGS_$(CLASS)) +DYNFLAGS += $(BLOCAL) $(ZNOVERSION) -Wl,-e_start +LDLIBS += -lc -lmapmalloc + +$(LIBS):= PICS += $(SHAREDOBJS) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/brand/sngl/sngl_brand/amd64/Makefile b/usr/src/lib/brand/sngl/sngl_brand/amd64/Makefile new file mode 100644 index 0000000000..d019eaf841 --- /dev/null +++ b/usr/src/lib/brand/sngl/sngl_brand/amd64/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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, Joyent, Inc. All rights reserved. +# + +ISAOBJDIR = $(BRAND_SHARED)/brand/amd64/pics/ + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +CPPFLAGS += -I$(BRAND_SHARED)/brand/amd64 + +# +# see ../Makefile.com for why we MUST explicity make ld.so.1 our interpreter +# +DYNFLAGS += -Wl,-I/lib/64/ld.so.1 +CPPFLAGS += -D_SYSCALL32 + +# Note that we also set the runtime path for the emulation library to point +# into /system. This is an attempt to ensure that our brand library get's the +# native version of libmapmallc. +# +DYNFLAGS += -R/lib/64 -R/system/usr/lib/64 + +CLEANFILES += $(DYNLIB) +CLOBBERFILES += $(ROOTLIBS64) + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/brand/sngl/sngl_brand/common/mapfile-vers b/usr/src/lib/brand/sngl/sngl_brand/common/mapfile-vers new file mode 100644 index 0000000000..29800204f8 --- /dev/null +++ b/usr/src/lib/brand/sngl/sngl_brand/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) 2006, 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 + +# +# Scope everything local -- our .init section is our only public interface. +# +SYMBOL_SCOPE { + local: + *; +}; diff --git a/usr/src/lib/brand/sngl/sngl_brand/common/sngl_brand.c b/usr/src/lib/brand/sngl/sngl_brand/common/sngl_brand.c new file mode 100644 index 0000000000..d2b8d8702d --- /dev/null +++ b/usr/src/lib/brand/sngl/sngl_brand/common/sngl_brand.c @@ -0,0 +1,429 @@ +/* + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <sys/brand.h> +#include <sys/syscall.h> +#include <sys/systm.h> +#include <sys/stat.h> +#include <libgen.h> +#include <sys/auxv.h> + +#include <sngl_brand.h> +#include <brand_misc.h> + +/* + * See usr/src/lib/brand/shared/brand/common/brand_util.c for general + * emulation notes. + */ + +#define CONF32_PATH "/system/usr/lib/brand/sngl/ld.sys.config" +#define CONF64_PATH "/system/usr/lib/brand/sngl/ld.sys64.config" + +brand_sysent_table_t brand_sysent_table[]; + +static boolean_t is_sys = B_FALSE; +static boolean_t is_crle = B_FALSE; + +typedef struct { + char *mnt_name; + dev_t mnt_id; +} sys_mnt_dev_t; + +/* + * The brand platform mounts several GZ file systems into the zone. We know + * which ones can actually be on seperate file systems, so we only stat those + * when checking for system commands. This reduces the number of stats needed + * when we start up. + */ +static sys_mnt_dev_t sys_mounts[] = { + {"/system/usr", 0}, + {"/lib", 0}, + {NULL, 0} +}; + +/* + * If this is a /sytem binary and ld.so is opening the default config file, + * then redirect so it opens the /system config file instead. We need to do it + * this way, instead of setting one of the LD_CONFIG env vars, since those + * are ignored for secure binaries. + * + * We don't redirect if we're crle so that it can still be used on the default + * config files. + */ +int +sngl_open(sysret_t *rval, char *path, int oflag, mode_t mode) +{ + char tstr[MAXPATHLEN]; + + if (is_sys && !is_crle) { + /* Get a copy of the path we're trying to open */ + bzero(tstr, sizeof (tstr)); + (void) brand_uucopystr(path, tstr, sizeof (tstr)); + + if (strcmp(tstr, "/var/ld/ld.config") == 0) + return (__systemcall(rval, SYS_open + 1024, + CONF32_PATH, oflag, mode)); + + if (strcmp(tstr, "/var/ld/64/ld.config") == 0) + return (__systemcall(rval, SYS_open + 1024, + CONF64_PATH, oflag, mode)); + } + + return (__systemcall(rval, SYS_open + 1024, path, oflag, mode)); +} + +/*ARGSUSED*/ +int +brand_init(int argc, char *argv[], char *envp[]) +{ + ulong_t ldentry; + int i; + uintptr_t *p; + auxv_t *ap; + struct stat64 buf; + char *bname; + + brand_pre_init(); + + /* + * Check if we're trying to run a system binary. + * + * We haven't installed our emulation table yet, so its safe to make + * system calls directly. + * + * First, get the /system devices, then stat the executable to see if + * its on one of the /system devs. + */ + for (i = 0; sys_mounts[i].mnt_name != NULL; i++) { + if (stat64(sys_mounts[i].mnt_name, &buf) != -1) + sys_mounts[i].mnt_id = buf.st_dev; + } + + /* Find the aux vector on the stack. */ + p = (uintptr_t *)envp; + while (*p != NULL) + p++; + p++; + + /* Find AT_SUN_EXECNAME */ + for (ap = (auxv_t *)p; ap->a_type != AT_NULL; ap++) { + if (ap->a_type != AT_SUN_EXECNAME) + continue; + if (stat64(ap->a_un.a_ptr, &buf) != -1) { + for (i = 0; sys_mounts[i].mnt_name != NULL; i++) { + if (sys_mounts[i].mnt_id == buf.st_dev) { + is_sys = B_TRUE; + bname = basename(ap->a_un.a_ptr); + if (strcmp("crle", bname) == 0) + is_crle = B_TRUE; + break; + } + } + } + break; + } + + ldentry = brand_post_init(SNGL_VERSION, argc, argv, envp); + + brand_runexe(argv, ldentry); + /*NOTREACHED*/ + brand_abort(0, "brand_runexe() returned"); + return (-1); +} + +/* + * This table must have at least NSYSCALL entries in it. + * + * The second parameter of each entry in the brand_sysent_table + * contains the number of parameters and flags that describe the + * syscall return value encoding. See the block comments at the + * top of this file for more information about the syscall return + * value flags and when they should be used. + */ +brand_sysent_table_t brand_sysent_table[] = { + NOSYS, /* 0 */ + NOSYS, /* 1 */ + NOSYS, /* 2 */ + NOSYS, /* 3 */ + NOSYS, /* 4 */ + EMULATE(sngl_open, 3 | RV_DEFAULT), /* 5 */ + NOSYS, /* 6 */ + NOSYS, /* 7 */ + NOSYS, /* 8 */ + NOSYS, /* 9 */ + NOSYS, /* 10 */ + NOSYS, /* 11 */ + NOSYS, /* 12 */ + NOSYS, /* 13 */ + NOSYS, /* 14 */ + NOSYS, /* 15 */ + NOSYS, /* 16 */ + NOSYS, /* 17 */ + NOSYS, /* 18 */ + NOSYS, /* 19 */ + NOSYS, /* 20 */ + NOSYS, /* 21 */ + NOSYS, /* 22 */ + NOSYS, /* 23 */ + NOSYS, /* 24 */ + NOSYS, /* 25 */ + NOSYS, /* 26 */ + NOSYS, /* 27 */ + NOSYS, /* 28 */ + NOSYS, /* 29 */ + NOSYS, /* 30 */ + NOSYS, /* 31 */ + NOSYS, /* 32 */ + NOSYS, /* 33 */ + NOSYS, /* 34 */ + NOSYS, /* 35 */ + NOSYS, /* 36 */ + NOSYS, /* 37 */ + NOSYS, /* 38 */ + NOSYS, /* 39 */ + NOSYS, /* 40 */ + NOSYS, /* 41 */ + NOSYS, /* 42 */ + NOSYS, /* 43 */ + NOSYS, /* 44 */ + NOSYS, /* 45 */ + NOSYS, /* 46 */ + NOSYS, /* 47 */ + NOSYS, /* 48 */ + NOSYS, /* 49 */ + NOSYS, /* 50 */ + NOSYS, /* 51 */ + NOSYS, /* 52 */ + NOSYS, /* 53 */ + NOSYS, /* 54 */ + NOSYS, /* 55 */ + NOSYS, /* 56 */ + NOSYS, /* 57 */ + NOSYS, /* 58 */ + NOSYS, /* 59 */ + NOSYS, /* 60 */ + NOSYS, /* 61 */ + NOSYS, /* 62 */ + NOSYS, /* 63 */ + NOSYS, /* 64 */ + NOSYS, /* 65 */ + NOSYS, /* 66 */ + NOSYS, /* 67 */ + NOSYS, /* 68 */ + NOSYS, /* 69 */ + NOSYS, /* 70 */ + NOSYS, /* 71 */ + NOSYS, /* 72 */ + NOSYS, /* 73 */ + NOSYS, /* 74 */ + NOSYS, /* 75 */ + NOSYS, /* 76 */ + NOSYS, /* 77 */ + NOSYS, /* 78 */ + NOSYS, /* 79 */ + NOSYS, /* 80 */ + NOSYS, /* 81 */ + NOSYS, /* 82 */ + NOSYS, /* 83 */ + NOSYS, /* 84 */ + NOSYS, /* 85 */ + NOSYS, /* 86 */ + NOSYS, /* 87 */ + NOSYS, /* 88 */ + NOSYS, /* 89 */ + NOSYS, /* 90 */ + NOSYS, /* 91 */ + NOSYS, /* 92 */ + NOSYS, /* 93 */ + NOSYS, /* 94 */ + NOSYS, /* 95 */ + NOSYS, /* 96 */ + NOSYS, /* 97 */ + NOSYS, /* 98 */ + NOSYS, /* 99 */ + NOSYS, /* 100 */ + NOSYS, /* 101 */ + NOSYS, /* 102 */ + NOSYS, /* 103 */ + NOSYS, /* 104 */ + NOSYS, /* 105 */ + NOSYS, /* 106 */ + NOSYS, /* 107 */ + NOSYS, /* 108 */ + NOSYS, /* 109 */ + NOSYS, /* 110 */ + NOSYS, /* 111 */ + NOSYS, /* 112 */ + NOSYS, /* 113 */ + NOSYS, /* 114 */ + NOSYS, /* 115 */ + NOSYS, /* 116 */ + NOSYS, /* 117 */ + NOSYS, /* 118 */ + NOSYS, /* 119 */ + NOSYS, /* 120 */ + NOSYS, /* 121 */ + NOSYS, /* 122 */ + NOSYS, /* 123 */ + NOSYS, /* 124 */ + NOSYS, /* 125 */ + NOSYS, /* 126 */ + NOSYS, /* 127 */ + NOSYS, /* 128 */ + NOSYS, /* 129 */ + NOSYS, /* 130 */ + NOSYS, /* 131 */ + NOSYS, /* 132 */ + NOSYS, /* 133 */ + NOSYS, /* 134 */ + NOSYS, /* 135 */ + NOSYS, /* 136 */ + NOSYS, /* 137 */ + NOSYS, /* 138 */ + NOSYS, /* 139 */ + NOSYS, /* 140 */ + NOSYS, /* 141 */ + NOSYS, /* 142 */ + NOSYS, /* 143 */ + NOSYS, /* 144 */ + NOSYS, /* 145 */ + NOSYS, /* 146 */ + NOSYS, /* 147 */ + NOSYS, /* 148 */ + NOSYS, /* 149 */ + NOSYS, /* 150 */ + NOSYS, /* 151 */ + NOSYS, /* 152 */ + NOSYS, /* 153 */ + NOSYS, /* 154 */ + NOSYS, /* 155 */ + NOSYS, /* 156 */ + NOSYS, /* 157 */ + NOSYS, /* 158 */ + NOSYS, /* 159 */ + NOSYS, /* 160 */ + NOSYS, /* 161 */ + NOSYS, /* 162 */ + NOSYS, /* 163 */ + NOSYS, /* 164 */ + NOSYS, /* 165 */ + NOSYS, /* 166 */ + NOSYS, /* 167 */ + NOSYS, /* 168 */ + NOSYS, /* 169 */ + NOSYS, /* 170 */ + NOSYS, /* 171 */ + NOSYS, /* 172 */ + NOSYS, /* 173 */ + NOSYS, /* 174 */ + NOSYS, /* 175 */ + NOSYS, /* 176 */ + NOSYS, /* 177 */ + NOSYS, /* 178 */ + NOSYS, /* 179 */ + NOSYS, /* 180 */ + NOSYS, /* 181 */ + NOSYS, /* 182 */ + NOSYS, /* 183 */ + NOSYS, /* 184 */ + NOSYS, /* 185 */ + NOSYS, /* 186 */ + NOSYS, /* 187 */ + NOSYS, /* 188 */ + NOSYS, /* 189 */ + NOSYS, /* 190 */ + NOSYS, /* 191 */ + NOSYS, /* 192 */ + NOSYS, /* 193 */ + NOSYS, /* 194 */ + NOSYS, /* 195 */ + NOSYS, /* 196 */ + NOSYS, /* 197 */ + NOSYS, /* 198 */ + NOSYS, /* 199 */ + NOSYS, /* 200 */ + NOSYS, /* 201 */ + NOSYS, /* 202 */ + NOSYS, /* 203 */ + NOSYS, /* 204 */ + NOSYS, /* 205 */ + NOSYS, /* 206 */ + NOSYS, /* 207 */ + NOSYS, /* 208 */ + NOSYS, /* 209 */ + NOSYS, /* 210 */ + NOSYS, /* 211 */ + NOSYS, /* 212 */ + NOSYS, /* 213 */ + NOSYS, /* 214 */ + NOSYS, /* 215 */ + NOSYS, /* 216 */ + NOSYS, /* 217 */ + NOSYS, /* 218 */ + NOSYS, /* 219 */ + NOSYS, /* 220 */ + NOSYS, /* 221 */ + NOSYS, /* 222 */ + NOSYS, /* 223 */ + NOSYS, /* 224 */ + NOSYS, /* 225 */ + NOSYS, /* 226 */ + NOSYS, /* 227 */ + NOSYS, /* 228 */ + NOSYS, /* 229 */ + NOSYS, /* 230 */ + NOSYS, /* 231 */ + NOSYS, /* 232 */ + NOSYS, /* 233 */ + NOSYS, /* 234 */ + NOSYS, /* 235 */ + NOSYS, /* 236 */ + NOSYS, /* 237 */ + NOSYS, /* 238 */ + NOSYS, /* 239 */ + NOSYS, /* 240 */ + NOSYS, /* 241 */ + NOSYS, /* 242 */ + NOSYS, /* 243 */ + NOSYS, /* 244 */ + NOSYS, /* 245 */ + NOSYS, /* 246 */ + NOSYS, /* 247 */ + NOSYS, /* 248 */ + NOSYS, /* 249 */ + NOSYS, /* 250 */ + NOSYS, /* 251 */ + NOSYS, /* 252 */ + NOSYS, /* 253 */ + NOSYS, /* 254 */ + NOSYS /* 255 */ +}; diff --git a/usr/src/lib/brand/sngl/sngl_brand/i386/Makefile b/usr/src/lib/brand/sngl/sngl_brand/i386/Makefile new file mode 100644 index 0000000000..a0c4927252 --- /dev/null +++ b/usr/src/lib/brand/sngl/sngl_brand/i386/Makefile @@ -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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, Joyent, Inc. All rights reserved. +# + +ISAOBJDIR = $(BRAND_SHARED)/brand/i386/pics + +include ../Makefile.com + +CPPFLAGS += -I$(BRAND_SHARED)/brand/i386 + +# +# see ../Makefile.com for why we explicity make ld.so.1 our interpreter +# +DYNFLAGS += -Wl,-I/lib/ld.so.1 + +# Note that we also set the runtime path for the emulation library to point +# into /system. This is an attempt to ensure that our brand library get's the +# native version of libmapmallc. +# +DYNFLAGS += -R/lib -R/system/usr/lib + +CLEANFILES += $(DYNLIB) +CLOBBERFILES += $(ROOTLIBS) + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/brand/sngl/zone/Makefile b/usr/src/lib/brand/sngl/zone/Makefile new file mode 100644 index 0000000000..42785deab3 --- /dev/null +++ b/usr/src/lib/brand/sngl/zone/Makefile @@ -0,0 +1,53 @@ +# +# 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. +# + +PROGS = sinstall +XMLDOCS= config.xml platform.xml +CLOBBERFILES= $(ROOTPROGS) $(ROOTXMLDOCS) + +include $(SRC)/cmd/Makefile.cmd +include ../Makefile.sngl + +.KEEP_STATE: + +all: $(PROGS) + +POFILES = +POFILE = + +install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) + crle -c $(ROOTBRANDDIR)/ld.sys.config \ + -l /lib:/system/usr/lib \ + -s /lib/secure:/system/usr/lib/secure + crle -64 -c $(ROOTBRANDDIR)/ld.sys64.config \ + -l /lib/64:/system/usr/lib/64 \ + -s /lib/secure/64:/system/usr/lib/secure/64 + +lint: + +clean: + -$(RM) $(PROGS) + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/lib/brand/sngl/zone/config.xml b/usr/src/lib/brand/sngl/zone/config.xml new file mode 100644 index 0000000000..b19f42932a --- /dev/null +++ b/usr/src/lib/brand/sngl/zone/config.xml @@ -0,0 +1,113 @@ +<?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="sngl"> + <modname>sngl_brand</modname> + + <initname>/system/sbin/init</initname> + <login_cmd>/system/usr/bin/login -z %Z %u</login_cmd> + <forcedlogin_cmd>/system/usr/bin/login -z %Z -f %u</forcedlogin_cmd> + <user_cmd>/system/usr/bin/getent passwd %u</user_cmd> + + <install>/usr/lib/brand/sngl/sinstall -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>/bin/true</attach> + <detach>/bin/true</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_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_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/sngl/zone/platform.xml b/usr/src/lib/brand/sngl/zone/platform.xml new file mode 100644 index 0000000000..94ba5ca59c --- /dev/null +++ b/usr/src/lib/brand/sngl/zone/platform.xml @@ -0,0 +1,161 @@ +<?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) 2012 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="sngl" 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="%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="/lib" directory="/system/lib" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/sbin" directory="/system/sbin" + opt="ro,nodevices" type="lofs" /> + <global_mount special="/usr" directory="/system/usr" + opt="ro,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" /> + + <!-- 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="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="tap" /> + <device match="tcp" /> + <device match="tcp6" /> + <device match="term" /> + <device match="ticlts" /> + <device match="ticots" /> + <device match="ticotsord" /> + <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/sngl/zone/sinstall.sh b/usr/src/lib/brand/sngl/zone/sinstall.sh new file mode 100644 index 0000000000..4928c36304 --- /dev/null +++ b/usr/src/lib/brand/sngl/zone/sinstall.sh @@ -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/common/i386/crtn.s b/usr/src/lib/common/i386/crtn.s index 6e37e25a8f..413c102006 100644 --- a/usr/src/lib/common/i386/crtn.s +++ b/usr/src/lib/common/i386/crtn.s @@ -33,7 +33,6 @@ * * For further details - see bug#4433015 */ - .ident "%Z%%M% %I% %E% SMI" .file "crtn.s" /* diff --git a/usr/src/lib/fm/topo/libtopo/common/libtopo.h b/usr/src/lib/fm/topo/libtopo/common/libtopo.h index 3ea35cdddd..e0adb6e0ab 100644 --- a/usr/src/lib/fm/topo/libtopo/common/libtopo.h +++ b/usr/src/lib/fm/topo/libtopo/common/libtopo.h @@ -22,6 +22,9 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #ifndef _LIBTOPO_H #define _LIBTOPO_H @@ -389,6 +392,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 converting sensor/indicator types, units, states, etc to diff --git a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers index b81f4fd7c6..c6ff800951 100644 --- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers +++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers @@ -76,6 +76,7 @@ SYMBOL_VERSION SUNWprivate { topo_hdl_prominfo; topo_hdl_strdup; topo_hdl_strfree; + topo_hdl_strsplit; topo_hdl_zalloc; topo_led_state_name; topo_led_type_name; @@ -118,6 +119,7 @@ SYMBOL_VERSION SUNWprivate { topo_mod_str2nvl; topo_mod_strdup; topo_mod_strfree; + topo_mod_strsplit; topo_mod_unload; topo_mod_unregister; topo_mod_walk_init; 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 2a137ad388..e6dda440a0 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h +++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h @@ -22,6 +22,9 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #ifndef _TOPO_MOD_H #define _TOPO_MOD_H @@ -215,6 +218,8 @@ extern void *topo_mod_zalloc(topo_mod_t *, size_t); 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 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 2c1f451770..2f59013e9e 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_string.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_string.c @@ -23,8 +23,9 @@ * Copyright 2006 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. + */ #include <strings.h> #include <ctype.h> @@ -56,6 +57,58 @@ topo_hdl_strfree(topo_hdl_t *thp, char *s) } 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)); @@ -67,6 +120,13 @@ topo_mod_strfree(topo_mod_t *mod, char *s) topo_hdl_strfree(mod->tm_hdl, s); } +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 000742db71..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 @@ -69,17 +69,19 @@ EOF enclosure=1 bay=0 slot=0 -devctl='/devices/pci@0,0/pci8086,3c02@1/pci15d9,691@0:devctl' +devctl0='/devices/pci@0,0/pci8086,3c02@1/pci15d9,691@0:devctl' +devctl1='/devices/pci@0,0/pci8086,e02@1/pci15d9,691@0:devctl' while (( slot <= 7 )); do - do_node $bay "Front Disk $bay" "$devctl" $enclosure $slot + do_node $bay "Front Disk $bay" "$devctl0|$devctl1" $enclosure $slot (( bay = bay + 1 )) (( slot = slot + 1 )) done slot=0 -devctl='/devices/pci@0,0/pci8086,3c06@2,2/pci15d9,691@0:devctl' +devctl0='/devices/pci@0,0/pci8086,3c06@2,2/pci15d9,691@0:devctl' +devctl1='/devices/pci@0,0/pci8086,e06@2,2/pci15d9,691@0:devctl' while (( slot <= 7 )); do - do_node $bay "Front Disk $bay" "$devctl" $enclosure $slot + do_node $bay "Front Disk $bay" "$devctl0|$devctl1" $enclosure $slot (( bay = bay + 1 )) (( slot = slot + 1 )) done 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 fda43bca6c..7d8f85fd88 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 @@ -2,7 +2,7 @@ <!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/topology.dtd.1"> <!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -Copyright (c) 2013, Joyent, Inc. All rights reserved. +Copyright (c) 2014, Joyent, Inc. All rights reserved. CDDL HEADER START @@ -152,7 +152,7 @@ Copyright (c) 2013, Joyent, Inc. All rights reserved. </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/krb5/plugins/preauth/pkinit/Makefile.com b/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com index 8449d22e24..07fcb2bbf7 100644 --- a/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com +++ b/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com @@ -70,7 +70,7 @@ CERRWARN += -_gcc=-Wno-unused-function CFLAGS += $(CCVERBOSE) -I.. DYNFLAGS += $(KRUNPATH) $(KMECHLIB) -znodelete -LDLIBS += -L $(ROOTLIBDIR) -lcrypto -lc +LDLIBS += -L $(ROOTLIBDIR) -lsunw_crypto -lc ROOTLIBDIR= $(ROOT)/usr/lib/krb5/plugins/preauth diff --git a/usr/src/lib/libbc/inc/include/sys/socket.h b/usr/src/lib/libbc/inc/include/sys/socket.h index 03961f805b..6607721e62 100644 --- a/usr/src/lib/libbc/inc/include/sys/socket.h +++ b/usr/src/lib/libbc/inc/include/sys/socket.h @@ -3,8 +3,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Copyright (c) 1982, 1985, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement @@ -169,6 +167,4 @@ struct msghdr { #define MSG_PEEK 0x2 /* peek at incoming message */ #define MSG_DONTROUTE 0x4 /* send without using routing tables */ -#define MSG_MAXIOVLEN 16 - #endif /*!_sys_socket_h*/ diff --git a/usr/src/lib/libbc/libc/sys/common/flock.c b/usr/src/lib/libbc/libc/sys/common/flock.c index 1c34632344..9c5f17e77d 100644 --- a/usr/src/lib/libbc/libc/sys/common/flock.c +++ b/usr/src/lib/libbc/libc/sys/common/flock.c @@ -22,10 +22,9 @@ /* * Copyright 1990 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/file.h> #include <sys/fcntl.h> @@ -33,7 +32,7 @@ int flock(fd, operation) int fd, operation; { struct flock fl; - int cmd = F_SETLKW; + int cmd = F_FLOCKW; fl.l_whence = 0; fl.l_start = 0; @@ -46,6 +45,6 @@ int fd, operation; if (operation & LOCK_EX) fl.l_type |= F_WRLCK; if (operation & LOCK_NB) - cmd = F_SETLK; + cmd = F_FLOCK; return(bc_fcntl(fd, cmd, &fl)); } diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c index 8d355e865a..e8fbe3d994 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> */ diff --git a/usr/src/lib/libbrand/common/libbrand.c b/usr/src/lib/libbrand/common/libbrand.c index 7d34df9e58..dc406d60a0 100644 --- a/usr/src/lib/libbrand/common/libbrand.c +++ b/usr/src/lib/libbrand/common/libbrand.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -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; @@ -810,13 +818,14 @@ brand_config_iter_privilege(brand_handle_t bh, } static int -i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonepath, - int (*func)(void *, const char *, const char *, const char *, - const char *), void *data, const xmlChar *mount_type) +i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename, + const char *zonepath, int (*func)(void *, const char *, const char *, + const char *, const char *), void *data, const xmlChar *mount_type) { xmlNodePtr node; xmlChar *special, *dir, *type, *opt; char special_exp[MAXPATHLEN]; + char dir_exp[MAXPATHLEN]; char opt_exp[MAXPATHLEN]; int ret; @@ -841,7 +850,11 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonepath, /* Substitute token values as needed. */ if ((ret = i_substitute_tokens((char *)special, special_exp, sizeof (special_exp), - NULL, zonepath, NULL, NULL)) != 0) + 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 */ @@ -851,11 +864,11 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonepath, } else { if ((ret = i_substitute_tokens((char *)opt, opt_exp, sizeof (opt_exp), - NULL, zonepath, NULL, NULL)) != 0) + zonename, zonepath, NULL, NULL)) != 0) 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: @@ -885,13 +898,13 @@ next: * %R Zonepath of zone */ int -brand_platform_iter_gmounts(brand_handle_t bh, const char *zonepath, - int (*func)(void *, const char *, const char *, const char *, - const char *), void *data) +brand_platform_iter_gmounts(brand_handle_t bh, const char *zonename, + const char *zonepath, int (*func)(void *, const char *, const char *, + const char *, const char *), void *data) { struct brand_handle *bhp = (struct brand_handle *)bh; - return (i_brand_platform_iter_mounts(bhp, zonepath, func, data, - DTD_ELEM_GLOBAL_MOUNT)); + return (i_brand_platform_iter_mounts(bhp, zonename, zonepath, func, + data, DTD_ELEM_GLOBAL_MOUNT)); } /* @@ -905,7 +918,7 @@ brand_platform_iter_mounts(brand_handle_t bh, int (*func)(void *, const char *, const char *, const char *, const char *), void *data) { struct brand_handle *bhp = (struct brand_handle *)bh; - return (i_brand_platform_iter_mounts(bhp, NULL, func, data, + return (i_brand_platform_iter_mounts(bhp, NULL, NULL, func, data, DTD_ELEM_MOUNT)); } diff --git a/usr/src/lib/libbrand/common/libbrand.h b/usr/src/lib/libbrand/common/libbrand.h index 5c5fb1b42e..30accf95ba 100644 --- a/usr/src/lib/libbrand/common/libbrand.h +++ b/usr/src/lib/libbrand/common/libbrand.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -106,8 +106,8 @@ extern int brand_config_iter_privilege(brand_handle_t, extern int brand_platform_iter_devices(brand_handle_t, const char *, int (*)(void *, const char *, const char *), void *, const char *); extern int brand_platform_iter_gmounts(brand_handle_t, const char *, - int (*)(void *, const char *, const char *, const char *, const char *), - void *); + const char *, int (*)(void *, const char *, const char *, const char *, + const char *), void *); extern int brand_platform_iter_link(brand_handle_t, int (*)(void *, const char *, const char *), void *); extern int brand_platform_iter_mounts(brand_handle_t, int (*)(void *, diff --git a/usr/src/lib/libbsm/common/adt.c b/usr/src/lib/libbsm/common/adt.c index 8c7b299e32..4cf0dd7566 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. */ #include <bsm/adt.h> @@ -702,7 +703,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)) { /* @@ -739,7 +770,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; /* @@ -747,58 +780,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); } /* @@ -2093,8 +2091,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..5214915c56 --- /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 (c) 2014 Joyent, Inc. All rights reserved. +# + +LIBRARY = libbunyan.a +VERS = .1 +OBJECTS = bunyan.o +USDT_PROVIDERS = bunyan_provider.d + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lumem -lnvpair -lnsl +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..149702a38f --- /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, ...) +{ + bunyan_t *b = (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(b, &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/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile index 3d649dfe9d..dc86e1a197 100644 --- a/usr/src/lib/libc/amd64/Makefile +++ b/usr/src/lib/libc/amd64/Makefile @@ -385,6 +385,7 @@ PORTGEN= \ fdetach.o \ fdopendir.o \ ffs.o \ + flock.o \ fls.o \ fmtmsg.o \ ftime.o \ @@ -863,13 +864,16 @@ PORTSYS= \ chmod.o \ chown.o \ corectl.o \ + epoll.o \ exacctsys.o \ execl.o \ execle.o \ execv.o \ + eventfd.o \ fcntl.o \ getpagesizes.o \ getpeerucred.o \ + inotify.o \ inst_sync.o \ issetugid.o \ label.o \ @@ -907,6 +911,7 @@ PORTSYS= \ tasksys.o \ time.o \ time_util.o \ + timerfd.o \ ucontext.o \ unlink.o \ ustat.o \ 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 beb568ebf6..9896614023 100644 --- a/usr/src/lib/libc/i386/Makefile.com +++ b/usr/src/lib/libc/i386/Makefile.com @@ -418,6 +418,7 @@ PORTGEN= \ fdetach.o \ fdopendir.o \ ffs.o \ + flock.o \ fls.o \ fmtmsg.o \ ftime.o \ @@ -903,6 +904,8 @@ PORTSYS= \ chmod.o \ chown.o \ corectl.o \ + epoll.o \ + eventfd.o \ exacctsys.o \ execl.o \ execle.o \ @@ -910,6 +913,7 @@ PORTSYS= \ fcntl.o \ getpagesizes.o \ getpeerucred.o \ + inotify.o \ inst_sync.o \ issetugid.o \ label.o \ @@ -947,6 +951,7 @@ PORTSYS= \ tasksys.o \ time.o \ time_util.o \ + timerfd.o \ ucontext.o \ unlink.o \ ustat.o \ 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 f7cdc6a6bd..66d811f25b 100644 --- a/usr/src/lib/libc/inc/thr_inlines.h +++ b/usr/src/lib/libc/inc/thr_inlines.h @@ -47,17 +47,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 de0d4a6b05..a58eaf915f 100644 --- a/usr/src/lib/libc/inc/thr_uberdata.h +++ b/usr/src/lib/libc/inc/thr_uberdata.h @@ -932,6 +932,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; @@ -944,6 +945,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 */ struct uberdata **tdb_bootstrap; tdb_t tdb; /* thread debug interfaces (for libc_db) */ } uberdata_t; @@ -1144,6 +1146,7 @@ typedef struct uberdata32 { int ndaemons; int pid; caddr32_t sigacthandler; + caddr32_t setctxt; caddr32_t lwp_stacks; caddr32_t lwp_laststack; int nfreestack; @@ -1243,6 +1246,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/flock.c b/usr/src/lib/libc/port/gen/flock.c new file mode 100644 index 0000000000..44dd12673f --- /dev/null +++ b/usr/src/lib/libc/port/gen/flock.c @@ -0,0 +1,60 @@ +/* + * 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 <sys/feature_tests.h> + +#include "lint.h" +#include <sys/types.h> +#include <sys/file.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +int +flock(int fildes, int operation) +{ + struct flock64 l; + int op; + int rv; + + l.l_whence = SEEK_SET; + l.l_start = 0; + l.l_len = 0; + l.l_sysid = 0; + l.l_pid = 0; + + switch (operation & ~LOCK_NB) { + case LOCK_UN: + if (operation & LOCK_NB) { + errno = EINVAL; + return (-1); + } + l.l_type = F_UNLCK; + rv = fcntl(fildes, F_FLOCK, &l); + break; + case LOCK_EX: + case LOCK_SH: + l.l_type = ((operation & ~LOCK_NB) == LOCK_EX) ? + F_WRLCK : F_RDLCK; + op = (operation & LOCK_NB) ? F_FLOCK : F_FLOCKW; + rv = fcntl(fildes, op, &l); + break; + default: + errno = EINVAL; + return (-1); + } + + return (rv); +} 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/getlogin.c b/usr/src/lib/libc/port/gen/getlogin.c index fadf3e055b..e4b2f62dc3 100644 --- a/usr/src/lib/libc/port/gen/getlogin.c +++ b/usr/src/lib/libc/port/gen/getlogin.c @@ -35,6 +35,7 @@ #include "lint.h" #include <sys/types.h> #include <sys/stat.h> +#include <sys/sysmacros.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> diff --git a/usr/src/lib/libc/port/gen/psiginfo.c b/usr/src/lib/libc/port/gen/psiginfo.c index a648c81094..e7cf46abef 100644 --- a/usr/src/lib/libc/port/gen/psiginfo.c +++ b/usr/src/lib/libc/port/gen/psiginfo.c @@ -51,16 +51,23 @@ psiginfo(const siginfo_t *sip, const char *s) { char buf[256]; char *c; + size_t l = 0; const struct siginfolist *listp; - if (sip == 0) + if (sip == NULL) return; + if (s != NULL && *s != '\0') { + l = snprintf(buf, sizeof (buf), _libc_gettext("%s : "), s); + if (l > sizeof (buf)) + l = sizeof (buf); + } + if (sip->si_code <= 0) { - (void) snprintf(buf, sizeof (buf), - _libc_gettext("%s : %s ( from process %d )\n"), - s, strsignal(sip->si_signo), sip->si_pid); + (void) snprintf(buf + l, sizeof (buf) - l, + _libc_gettext("%s ( from process %d )\n"), + strsignal(sip->si_signo), sip->si_pid); } else if (((listp = &_sys_siginfolist[sip->si_signo-1]) != NULL) && sip->si_code <= listp->nsiginfo) { c = _libc_gettext(listp->vsiginfo[sip->si_code-1]); @@ -69,21 +76,20 @@ psiginfo(const siginfo_t *sip, const char *s) case SIGBUS: case SIGILL: case SIGFPE: - (void) snprintf(buf, sizeof (buf), - _libc_gettext("%s : %s ( [%p] %s)\n"), - s, strsignal(sip->si_signo), + (void) snprintf(buf + l, sizeof (buf) - l, + _libc_gettext("%s ( [%p] %s)\n"), + strsignal(sip->si_signo), sip->si_addr, c); break; default: - (void) snprintf(buf, sizeof (buf), - _libc_gettext("%s : %s (%s)\n"), - s, strsignal(sip->si_signo), c); + (void) snprintf(buf + l, sizeof (buf) - l, + _libc_gettext("%s (%s)\n"), + strsignal(sip->si_signo), c); break; } } else { - (void) snprintf(buf, sizeof (buf), - _libc_gettext("%s : %s\n"), - s, strsignal(sip->si_signo)); + (void) snprintf(buf + l, sizeof (buf) - l, + _libc_gettext("%s\n"), strsignal(sip->si_signo)); } (void) write(2, buf, strlen(buf)); } diff --git a/usr/src/lib/libc/port/gen/psignal.c b/usr/src/lib/libc/port/gen/psignal.c index 201beaacd7..2a61cd9610 100644 --- a/usr/src/lib/libc/port/gen/psignal.c +++ b/usr/src/lib/libc/port/gen/psignal.c @@ -37,8 +37,6 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Print the name of the signal indicated by "sig", along with the * supplied message @@ -61,14 +59,15 @@ void psignal(int sig, const char *s) { char *c; - size_t n; + size_t n = 0; char buf[256]; if (sig < 0 || sig >= NSIG) sig = 0; c = strsignal(sig); - n = strlen(s); - if (n) { + if (s != NULL) + n = strlen(s); + if (n != 0) { (void) snprintf(buf, sizeof (buf), "%s: %s\n", s, c); } else { (void) snprintf(buf, sizeof (buf), "%s\n", c); 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/llib-lc b/usr/src/lib/libc/port/llib-lc index 9fb5935f7c..41c7ecf879 100644 --- a/usr/src/lib/libc/port/llib-lc +++ b/usr/src/lib/libc/port/llib-lc @@ -25,6 +25,7 @@ * Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved. * Copyright (c) 2013 Gary Mills * Copyright 2014 Garrett D'Amore <garrett@damore.org> + * Copyright 2015 Joyent, Inc. * Copyright 2015 Circonus, Inc. All rights reserved. */ @@ -427,6 +428,9 @@ int fdetach(const char *path); /* ffs.c */ int ffs(int field); +/* flock.c */ +int flock(int filedes, int operation); + /* fmtmsg.c */ int addseverity(int value, const char *string); int fmtmsg(long class, const char *label, int severity, const char *text, diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index e133407665..b112880009 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -2834,8 +2834,16 @@ $endif _dgettext; _doprnt; _doscan; + epoll_create; + epoll_create1; + epoll_ctl; + epoll_wait; + epoll_pwait; _errfp; _errxfp; + eventfd; + eventfd_read; + eventfd_write; exportfs; _F_cplx_div; _F_cplx_div_ix; @@ -2847,6 +2855,7 @@ $endif _findiop; __fini_daemon_priv; _finite; + flock; _fork1 { FLAGS = NODYNSORT }; _forkall { FLAGS = NODYNSORT }; _fpclass; @@ -2880,6 +2889,10 @@ $endif __idmap_unreg; __init_daemon_priv; __init_suid_priv; + inotify_init; + inotify_init1; + inotify_add_watch; + inotify_rm_watch; _insert; inst_sync; _iswctype; @@ -2956,6 +2969,7 @@ $endif scrwidth; semctl64; _semctl64; + set_escaped_context_cleanup; set_setcontext_enforcement; _setbufend; __set_errno; @@ -3018,6 +3032,9 @@ $endif _thr_suspend_mutator; thr_wait_mutator; _thr_wait_mutator; + timerfd_create; + timerfd_gettime; + timerfd_settime; __tls_get_addr; _tmem_get_base; _tmem_get_nentries; @@ -3073,6 +3090,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/sys/epoll.c b/usr/src/lib/libc/port/sys/epoll.c new file mode 100644 index 0000000000..93379b583e --- /dev/null +++ b/usr/src/lib/libc/port/sys/epoll.c @@ -0,0 +1,207 @@ +/* + * 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/types.h> +#include <sys/epoll.h> +#include <sys/devpoll.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> + +/* + * Events that match their epoll(7) equivalents. + */ +#if EPOLLIN != POLLIN +#error value of EPOLLIN does not match value of POLLIN +#endif + +#if EPOLLPRI != POLLPRI +#error value of EPOLLPRI does not match value of POLLPRI +#endif + +#if EPOLLOUT != POLLOUT +#error value of EPOLLOUT does not match value of POLLOUT +#endif + +#if EPOLLRDNORM != POLLRDNORM +#error value of EPOLLRDNORM does not match value of POLLRDNORM +#endif + +#if EPOLLRDBAND != POLLRDBAND +#error value of EPOLLRDBAND does not match value of POLLRDBAND +#endif + +#if EPOLLERR != POLLERR +#error value of EPOLLERR does not match value of POLLERR +#endif + +#if EPOLLHUP != POLLHUP +#error value of EPOLLHUP does not match value of POLLHUP +#endif + +/* + * Events that we ignore entirely. They can be set in events, but they will + * never be returned. + */ +#define EPOLLIGNORED (EPOLLMSG | EPOLLWAKEUP) + +/* + * Events that we swizzle into other bit positions. + */ +#define EPOLLSWIZZLED \ + (EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM) + +int +epoll_create(int size) +{ + int fd; + + /* + * From the epoll_create() man page: "Since Linux 2.6.8, the size + * argument is ignored, but must be greater than zero." You keep using + * that word "ignored"... + */ + if (size <= 0) { + errno = EINVAL; + return (-1); + } + + if ((fd = open("/dev/poll", O_RDWR)) == -1) + return (-1); + + if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) { + (void) close(fd); + return (-1); + } + + return (fd); +} + +int +epoll_create1(int flags) +{ + int fd, oflags = O_RDWR; + + if (flags & EPOLL_CLOEXEC) + oflags |= O_CLOEXEC; + + if ((fd = open("/dev/poll", oflags)) == -1) + return (-1); + + if (ioctl(fd, DP_EPOLLCOMPAT, 0) == -1) { + (void) close(fd); + return (-1); + } + + return (fd); +} + +int +epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + dvpoll_epollfd_t epoll[2]; + uint32_t events, ev = 0; + int i = 0; + + epoll[i].dpep_pollfd.fd = fd; + + switch (op) { + case EPOLL_CTL_DEL: + ev = POLLREMOVE; + break; + + case EPOLL_CTL_MOD: + /* + * In the modify case, we pass down two events: one to + * remove the event and another to add it back. + */ + epoll[i++].dpep_pollfd.events = POLLREMOVE; + epoll[i].dpep_pollfd.fd = fd; + /* FALLTHROUGH */ + + case EPOLL_CTL_ADD: + /* + * Mask off the events that we ignore, and then swizzle the + * events for which our values differ from their epoll(7) + * equivalents. + */ + events = event->events; + ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED); + + if (events & EPOLLRDHUP) + ev |= POLLRDHUP; + + if (events & EPOLLET) + ev |= POLLET; + + if (events & EPOLLONESHOT) + ev |= POLLONESHOT; + + if (events & EPOLLWRNORM) + ev |= POLLWRNORM; + + if (events & EPOLLWRBAND) + ev |= POLLWRBAND; + + epoll[i].dpep_data = event->data.u64; + break; + + default: + errno = EOPNOTSUPP; + return (-1); + } + + epoll[i].dpep_pollfd.events = ev; + + return (write(epfd, epoll, sizeof (epoll[0]) * (i + 1)) == -1 ? -1 : 0); +} + +int +epoll_wait(int epfd, struct epoll_event *events, + int maxevents, int timeout) +{ + struct dvpoll arg; + + if (maxevents <= 0) { + errno = EINVAL; + return (-1); + } + + arg.dp_nfds = maxevents; + arg.dp_timeout = timeout; + arg.dp_fds = (pollfd_t *)events; + + return (ioctl(epfd, DP_POLL, &arg)); +} + +int +epoll_pwait(int epfd, struct epoll_event *events, + int maxevents, int timeout, const sigset_t *sigmask) +{ + struct dvpoll arg; + + if (maxevents <= 0) { + errno = EINVAL; + return (-1); + } + + arg.dp_nfds = maxevents; + arg.dp_timeout = timeout; + arg.dp_fds = (pollfd_t *)events; + arg.dp_setp = (sigset_t *)sigmask; + + return (ioctl(epfd, DP_PPOLL, &arg)); +} diff --git a/usr/src/lib/libc/port/sys/eventfd.c b/usr/src/lib/libc/port/sys/eventfd.c new file mode 100644 index 0000000000..f165491cc1 --- /dev/null +++ b/usr/src/lib/libc/port/sys/eventfd.c @@ -0,0 +1,67 @@ +/* + * 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. All rights reserved. + */ + +#include <sys/eventfd.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +int +eventfd(unsigned int initval, int flags) +{ + int oflags = O_RDWR; + uint64_t val = initval; + int fd; + + if (flags & ~(EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE)) { + errno = EINVAL; + return (-1); + } + + if (flags & EFD_NONBLOCK) + oflags |= O_NONBLOCK; + + if (flags & EFD_CLOEXEC) + oflags |= O_CLOEXEC; + + if ((fd = open("/dev/eventfd", oflags)) < 0) + return (-1); + + if ((flags & EFD_SEMAPHORE) && + ioctl(fd, EVENTFDIOC_SEMAPHORE, 0) != 0) { + (void) close(fd); + return (-1); + } + + if (write(fd, &val, sizeof (val)) < sizeof (val)) { + (void) close(fd); + return (-1); + } + + return (fd); +} + +int +eventfd_read(int fd, eventfd_t *valp) +{ + return (read(fd, valp, sizeof (*valp)) < sizeof (*valp) ? -1 : 0); +} + +int +eventfd_write(int fd, eventfd_t val) +{ + return (write(fd, &val, sizeof (val)) < sizeof (val) ? -1 : 0); +} 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/timerfd.c b/usr/src/lib/libc/port/sys/timerfd.c new file mode 100644 index 0000000000..cb2e17adf7 --- /dev/null +++ b/usr/src/lib/libc/port/sys/timerfd.c @@ -0,0 +1,93 @@ +/* + * 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. All rights reserved. + */ + +#include <sys/timerfd.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +int +timerfd_create(int clockid, int flags) +{ + int oflags = O_RDWR; + int fd; + + if (flags & ~(TFD_NONBLOCK | TFD_CLOEXEC)) { + errno = EINVAL; + return (-1); + } + + if (flags & TFD_NONBLOCK) + oflags |= O_NONBLOCK; + + if (flags & TFD_CLOEXEC) + oflags |= O_CLOEXEC; + + if ((fd = open("/dev/timerfd", oflags)) < 0) + return (-1); + + if (ioctl(fd, TIMERFDIOC_CREATE, clockid) != 0) { + (void) close(fd); + return (-1); + } + + return (fd); +} + +int +timerfd_settime(int fd, int flags, const struct itimerspec *new_value, + struct itimerspec *old_value) +{ + timerfd_settime_t st; + int rval; + + if (flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) { + errno = EINVAL; + return (-1); + } + + st.tfd_settime_flags = flags; + st.tfd_settime_value = (uint64_t)(uintptr_t)new_value; + st.tfd_settime_ovalue = (uint64_t)(uintptr_t)old_value; + + rval = ioctl(fd, TIMERFDIOC_SETTIME, &st); + + if (rval == -1 && errno == ENOTTY) { + /* + * Linux has us return EINVAL when the file descriptor is valid + * but is not a timerfd file descriptor -- and LTP explicitly + * checks this case. + */ + errno = EINVAL; + } + + return (rval); +} + +int +timerfd_gettime(int fd, struct itimerspec *curr_value) +{ + int rval = ioctl(fd, TIMERFDIOC_GETTIME, curr_value); + + if (rval == -1 && errno == ENOTTY) { + /* + * See comment in timerfd_settime(), above. + */ + errno = EINVAL; + } + + return (rval); +} 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/alloc.c b/usr/src/lib/libc/port/threads/alloc.c index 5851212ad4..99f68000d2 100644 --- a/usr/src/lib/libc/port/threads/alloc.c +++ b/usr/src/lib/libc/port/threads/alloc.c @@ -21,13 +21,14 @@ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. */ #include "lint.h" #include "thr_uberdata.h" #include <sys/syscall.h> -extern int __systemcall6(sysret_t *, int, ...); +extern long __systemcall6(sysret_t *, int, ...); /* * This is a small and simple power of two memory allocator that is diff --git a/usr/src/lib/libc/port/threads/sigaction.c b/usr/src/lib/libc/port/threads/sigaction.c index 8e9df7ab60..09be90e54f 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; } /* @@ -522,6 +545,7 @@ int setcontext(const ucontext_t *ucp) { ulwp_t *self = curthread; + uberdata_t *udp = self->ul_uberdata; int ret; ucontext_t uc; @@ -578,7 +602,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 b5d848449d..485d7f8edf 100644 --- a/usr/src/lib/libc/port/threads/thr.c +++ b/usr/src/lib/libc/port/threads/thr.c @@ -23,7 +23,7 @@ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2014 Joyent, Inc. All rights reserved. */ #include "lint.h" @@ -124,6 +124,7 @@ uberdata_t __uberdata = { 0, /* ndaemons */ 0, /* pid */ sigacthandler, /* sigacthandler */ + __setcontext, /* setctxt */ NULL, /* lwp_stacks */ NULL, /* lwp_laststack */ 0, /* nfreestack */ @@ -136,6 +137,7 @@ uberdata_t __uberdata = { NULL, /* robustlocks */ NULL, /* robustlist */ NULL, /* progname */ + NULL, /* ub_broot */ NULL, /* __tdb_bootstrap */ { /* tdb */ NULL, /* tdb_sync_addr_hash */ @@ -1219,6 +1221,24 @@ extern void atfork_init(void); extern void __proc64id(void); #endif +static void +init_brandroot(uberdata_t *udp) +{ + Dl_argsinfo_t args; + + udp->ub_broot = 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_BRAND_NROOT) { + udp->ub_broot = args.dla_auxv->a_un.a_ptr; + return; + } + args.dla_auxv++; + } +} + /* * libc_init() is called by ld.so.1 for library initialization. * We perform minimal initialization; enough to work with the main thread. @@ -1255,6 +1275,13 @@ libc_init(void) (void) _atexit(__cleanup); /* + * Every libc, regardless of link map, needs to go through and check its + * aux vectors so as to indicate whether or not this has been given a + * brand root with which we use to qualify various other data. + */ + init_brandroot(udp); + + /* * We keep our uberdata on one of (a) the first alternate link map * or (b) the primary link map. We switch to the primary link map * and stay there once we see it. All intermediate link maps are diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com index 2991bb2d4d..1e54306c85 100644 --- a/usr/src/lib/libc/sparc/Makefile.com +++ b/usr/src/lib/libc/sparc/Makefile.com @@ -449,6 +449,7 @@ PORTGEN= \ fdetach.o \ fdopendir.o \ ffs.o \ + flock.o \ fls.o \ fmtmsg.o \ ftime.o \ @@ -937,6 +938,8 @@ PORTSYS= \ chmod.o \ chown.o \ corectl.o \ + epoll.o \ + eventfd.o \ exacctsys.o \ execl.o \ execle.o \ @@ -944,6 +947,7 @@ PORTSYS= \ fcntl.o \ getpagesizes.o \ getpeerucred.o \ + inotify.o \ inst_sync.o \ issetugid.o \ label.o \ @@ -981,6 +985,7 @@ PORTSYS= \ tasksys.o \ time.o \ time_util.o \ + timerfd.o \ ucontext.o \ unlink.o \ ustat.o \ diff --git a/usr/src/lib/libc/sparc/crt/_rtld.c b/usr/src/lib/libc/sparc/crt/_rtld.c index a9e9c6d98a..843cfe03a5 100644 --- a/usr/src/lib/libc/sparc/crt/_rtld.c +++ b/usr/src/lib/libc/sparc/crt/_rtld.c @@ -62,6 +62,15 @@ #define SYSCONFIG (*(funcs[SYSCONFIG_F])) /* + * GCC will not emit unused static functions unless specifically told it must + */ +#ifdef __GNUC__ +#define __USED __attribute__((used)) +#else +#define __USED +#endif + +/* * Alias ld.so entry point -- receives a bootstrap structure and a vector * of strings. The vector is "well-known" to us, and consists of pointers * to string constants. This aliasing bootstrap requires no relocation in 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 255121cab6..b8e5e9670b 100644 --- a/usr/src/lib/libc/sparcv9/Makefile.com +++ b/usr/src/lib/libc/sparcv9/Makefile.com @@ -409,6 +409,7 @@ PORTGEN= \ fdetach.o \ fdopendir.o \ ffs.o \ + flock.o \ fls.o \ fmtmsg.o \ ftime.o \ @@ -881,6 +882,7 @@ PORTSYS= \ chmod.o \ chown.o \ corectl.o \ + epoll.o \ exacctsys.o \ execl.o \ execle.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/libc_db/common/thread_db.c b/usr/src/lib/libc_db/common/thread_db.c index 77c44b2782..b58971dfec 100644 --- a/usr/src/lib/libc_db/common/thread_db.c +++ b/usr/src/lib/libc_db/common/thread_db.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ + #include <stdio.h> #include <stdlib.h> #include <stddef.h> @@ -146,6 +150,7 @@ static td_err_e td_read_uberdata(td_thragent_t *ta_p) { struct ps_prochandle *ph_p = ta_p->ph_p; + int i; if (ta_p->model == PR_MODEL_NATIVE) { uberdata_t uberdata; @@ -163,12 +168,10 @@ td_read_uberdata(td_thragent_t *ta_p) if (ps_pdread(ph_p, (psaddr_t)uberdata.tdb.tdb_events, ta_p->tdb_events, sizeof (ta_p->tdb_events)) != PS_OK) return (TD_DBERR); - } else { #if defined(_LP64) && defined(_SYSCALL32) uberdata32_t uberdata; caddr32_t tdb_events[TD_MAX_EVENT_NUM - TD_MIN_EVENT_NUM + 1]; - int i; if (ps_pdread(ph_p, ta_p->uberdata_addr, &uberdata, sizeof (uberdata)) != PS_OK) @@ -189,6 +192,29 @@ td_read_uberdata(td_thragent_t *ta_p) return (TD_DBERR); #endif } + + /* + * Unfortunately, we are (implicitly) assuming that our uberdata + * definition precisely matches that of our target. If this is not + * true (that is, if we're examining a core file from a foreign + * system that has a different definition of uberdata), the failure + * modes can be frustratingly non-explicit. In an effort to catch + * this upon initialization (when the debugger may still be able to + * opt for another thread model or may be able to fail explicitly), we + * check that each of our tdb_events points to valid memory (these are + * putatively text upon which a breakpoint can be issued), with the + * hope that this is enough of a self-consistency check to lead to + * explicit failure on a mismatch. + */ + for (i = 0; i < TD_MAX_EVENT_NUM - TD_MIN_EVENT_NUM + 1; i++) { + uint8_t check; + + if (ps_pdread(ph_p, (psaddr_t)ta_p->tdb_events[i], + &check, sizeof (check)) != PS_OK) { + return (TD_DBERR); + } + } + if (ta_p->hash_size != 1) { /* multi-threaded */ ta_p->initialized = 2; ta_p->single_lwpid = 0; diff --git a/usr/src/lib/libcmdutils/common/custr.c b/usr/src/lib/libcmdutils/common/custr.c index 1ec72de9dd..03a9561934 100644 --- a/usr/src/lib/libcmdutils/common/custr.c +++ b/usr/src/lib/libcmdutils/common/custr.c @@ -20,13 +20,20 @@ #include <stdlib.h> #include <err.h> #include <string.h> +#include <stdio.h> +#include <stdarg.h> #include "libcmdutils.h" +typedef enum { + CUSTR_FIXEDBUF = 0x01 +} custr_flags_t; + struct custr { size_t cus_strlen; size_t cus_datalen; char *cus_data; + custr_flags_t cus_flags; }; #define STRING_CHUNK_SIZE 64 @@ -53,23 +60,15 @@ custr_cstr(custr_t *cus) return (cus->cus_data); } -int -custr_appendc(custr_t *cus, char newc) -{ - char news[2]; - - news[0] = newc; - news[1] = '\0'; - - return (custr_append(cus, news)); -} - -int -custr_append(custr_t *cus, const char *news) +static int +custr_append_vprintf(custr_t *cus, const char *fmt, va_list ap) { - size_t len = strlen(news); + int len = vsnprintf(NULL, 0, fmt, ap); size_t chunksz = STRING_CHUNK_SIZE; + if (len == -1) + return (len); + while (chunksz < len) { chunksz *= 2; } @@ -78,6 +77,11 @@ custr_append(custr_t *cus, const char *news) char *new_data; size_t new_datalen = cus->cus_datalen + chunksz; + if (cus->cus_flags & CUSTR_FIXEDBUF) { + errno = EOVERFLOW; + return (-1); + } + /* * Allocate replacement memory: */ @@ -104,13 +108,41 @@ custr_append(custr_t *cus, const char *news) /* * Append new string to existing string: */ - (void) memcpy(cus->cus_data + cus->cus_strlen, news, len + 1); + len = vsnprintf(cus->cus_data + cus->cus_strlen, + (uintptr_t)cus->cus_data - (uintptr_t)cus->cus_strlen, fmt, ap); + if (len == -1) + return (len); cus->cus_strlen += len; return (0); } int +custr_appendc(custr_t *cus, char newc) +{ + return (custr_append_printf(cus, "%c", newc)); +} + +int +custr_append_printf(custr_t *cus, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = custr_append_vprintf(cus, fmt, ap); + va_end(ap); + + return (ret); +} + +int +custr_append(custr_t *cus, const char *name) +{ + return (custr_append_printf(cus, "%s", name)); +} + +int custr_alloc(custr_t **cus) { custr_t *t; @@ -124,12 +156,35 @@ custr_alloc(custr_t **cus) return (0); } +int +custr_alloc_buf(custr_t **cus, void *buf, size_t buflen) +{ + int ret; + + if (buflen == 0 || buf == NULL) { + errno = EINVAL; + return (-1); + } + + if ((ret = custr_alloc(cus)) != 0) + return (ret); + + (*cus)->cus_data = buf; + (*cus)->cus_datalen = buflen; + (*cus)->cus_strlen = 0; + (*cus)->cus_flags = CUSTR_FIXEDBUF; + (*cus)->cus_data[0] = '\0'; + + return (0); +} + void custr_free(custr_t *cus) { if (cus == NULL) return; - free(cus->cus_data); + if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0) + free(cus->cus_data); free(cus); } diff --git a/usr/src/lib/libcmdutils/common/mapfile-vers b/usr/src/lib/libcmdutils/common/mapfile-vers index 640959e4b5..3106695eb0 100644 --- a/usr/src/lib/libcmdutils/common/mapfile-vers +++ b/usr/src/lib/libcmdutils/common/mapfile-vers @@ -43,8 +43,10 @@ SYMBOL_VERSION SUNWprivate_1.1 { global: add_tnode; custr_alloc; + custr_alloc_buf; custr_append; custr_appendc; + custr_append_printf; custr_cstr; custr_free; custr_len; diff --git a/usr/src/lib/libcmdutils/libcmdutils.h b/usr/src/lib/libcmdutils/libcmdutils.h index bbc03475dc..e0c97178dc 100644 --- a/usr/src/lib/libcmdutils/libcmdutils.h +++ b/usr/src/lib/libcmdutils/libcmdutils.h @@ -156,6 +156,12 @@ extern int custr_alloc(custr_t **); extern void custr_free(custr_t *); /* + * Allocate a "custr_t" dynamic string object that operates on a fixed external + * buffer. + */ +extern int custr_alloc_buf(custr_t **, void *, size_t); + +/* * Append a single character, or a NUL-terminated string of characters, to a * dynamic string. Returns 0 on success and -1 otherwise. The dynamic string * will be unmodified if the function returns -1. @@ -164,6 +170,13 @@ extern int custr_appendc(custr_t *, char); extern int custr_append(custr_t *, const char *); /* + * Append a format string and arguments as though the contents were being parsed + * through snprintf. Returns 0 on success and -1 otherwise. The dynamic string + * will be unmodified if the function returns -1. + */ +extern int custr_append_printf(custr_t *, const char *, ...); + +/* * Determine the length in bytes, not including the NUL terminator, of the * dynamic string. */ diff --git a/usr/src/lib/libctf/Makefile.com b/usr/src/lib/libctf/Makefile.com index 4d1e01d4eb..0169c2a367 100644 --- a/usr/src/lib/libctf/Makefile.com +++ b/usr/src/lib/libctf/Makefile.com @@ -23,43 +23,9 @@ # Use is subject to license terms. # -LIBRARY = libctf.a -VERS = .1 - -COMMON_OBJS = \ - ctf_create.o \ - ctf_decl.o \ - ctf_error.o \ - ctf_hash.o \ - ctf_labels.o \ - ctf_lookup.o \ - ctf_open.o \ - ctf_types.o \ - ctf_util.o - -LIB_OBJS = \ - ctf_lib.o \ - ctf_subr.o - -OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) - -include ../../Makefile.lib +include ../Makefile.shared.com include ../../Makefile.rootfs -SRCS = $(COMMON_OBJS:%.o=../../../common/ctf/%.c) $(LIB_OBJS:%.o=../common/%.c) -LIBS = $(DYNLIB) $(LINTLIB) - -SRCDIR = ../common - -CPPFLAGS += -I../common -I../../../common/ctf -DCTF_OLD_VERSIONS -CFLAGS += $(CCVERBOSE) - -CERRWARN += -_gcc=-Wno-uninitialized - -LDLIBS += -lc - -$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) - .KEEP_STATE: all: $(LIBS) @@ -67,7 +33,4 @@ all: $(LIBS) lint: lintcheck include ../../Makefile.targ - -objs/%.o pics/%.o: ../../../common/ctf/%.c - $(COMPILE.c) -o $@ $< - $(POST_PROCESS_O) +include ../Makefile.shared.targ diff --git a/usr/src/lib/libctf/Makefile.shared.com b/usr/src/lib/libctf/Makefile.shared.com new file mode 100644 index 0000000000..a0370fc944 --- /dev/null +++ b/usr/src/lib/libctf/Makefile.shared.com @@ -0,0 +1,79 @@ +# +# 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 (c) 2015, Joyent, Inc. All rights reserved. +# + +# +# This Makefile is shared between the libctf native build in tools and +# the libctf build here for the system. +# +LIBRARY = libctf.a +VERS = .1 + +COMMON_OBJS = \ + ctf_create.o \ + ctf_decl.o \ + ctf_error.o \ + ctf_hash.o \ + ctf_labels.o \ + ctf_lookup.o \ + ctf_open.o \ + ctf_types.o \ + ctf_util.o + +LIST_OBJS = \ + list.o + +LIB_OBJS = \ + ctf_elfwrite.o \ + ctf_diff.o \ + ctf_lib.o \ + ctf_merge.o \ + ctf_subr.o + +OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) $(LIST_OBJS) +MAPFILEDIR = $(SRC)/lib/libctf + +include $(SRC)/lib/Makefile.lib + +SRCS = \ + $(COMMON_OBJS:%.o=$(SRC)/common/ctf/%.c) \ + $(LIB_OBJS:%.o=$(SRC)/lib/libctf/common/%.c) \ + $(LIST_OBJS:%.o=$(SRC)/common/list/%.c) \ + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lelf + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +SRCDIR = $(SRC)/lib/libctf/common + +CPPFLAGS += -I$(SRC)/lib/libctf/common -I$(SRC)/common/ctf -DCTF_OLD_VERSIONS +CFLAGS += $(CCVERBOSE) + +CERRWARN += -_gcc=-Wno-uninitialized + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) diff --git a/usr/src/lib/libctf/Makefile.shared.targ b/usr/src/lib/libctf/Makefile.shared.targ new file mode 100644 index 0000000000..282be86075 --- /dev/null +++ b/usr/src/lib/libctf/Makefile.shared.targ @@ -0,0 +1,26 @@ +# +# 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. All rights reserved. +# + +# +# This Makefile is shared between both the tools and the normal library build. +# + +pics/%.o: $(SRC)/common/ctf/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +pics/%.o: $(SRC)/common/list/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/libctf/common/ctf_diff.c b/usr/src/lib/libctf/common/ctf_diff.c new file mode 100644 index 0000000000..e819fe02cb --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_diff.c @@ -0,0 +1,1277 @@ +/* + * 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. All rights reserved. + */ + +/* + * The following ia a basic overview of how we diff types in containers (the + * generally interesting part of diff, and what's used by merge). We maintain + * two mapping tables, a table of forward mappings (src->dest), and a reverse + * mapping (dest->src). Both are initialized to contain no mapping, and can also + * be updated to contain a negative mapping. + * + * What we do first is iterate over each type in the src container, and compare + * it with a type in the destination container. This may involve doing recursive + * comparisons -- which can involve cycles. To deal with this, whenever we + * encounter something which may be cyclic, we insert a guess. In other words, + * we assume that it may be true. This is necessary for the classic case of the + * following structure: + * + * struct foo { + * struct foo *foo_next; + * }; + * + * If it turns out that we were wrong, we discard our guesses. + * + * If we find that a given type in src has no corresponding entry in dst, we + * then mark its map as CTF_ERR (-1) to indicate that it has *no* match, as + * opposed to the default value of 0, which indicates an unknown match. + * Once we've done the first iteration through src, we know at that point in + * time whether everything in dst is similar or not and can simply walk over it + * and don't have to do any additional checks. + */ + +#include <libctf.h> +#include <ctf_impl.h> +#include <sys/debug.h> + +typedef struct ctf_diff_func { + const char *cdf_name; + ulong_t cdf_symidx; + ulong_t cdf_matchidx; +} ctf_diff_func_t; + +typedef struct ctf_diff_obj { + const char *cdo_name; + ulong_t cdo_symidx; + ctf_id_t cdo_id; + ulong_t cdo_matchidx; +} ctf_diff_obj_t; + +typedef struct ctf_diff_guess { + struct ctf_diff_guess *cdg_next; + ctf_id_t cdg_iid; + ctf_id_t cdg_oid; +} ctf_diff_guess_t; + +/* typedef in libctf.h */ +struct ctf_diff { + uint_t cds_flags; + boolean_t cds_tvalid; /* types valid */ + ctf_file_t *cds_ifp; + ctf_file_t *cds_ofp; + ctf_id_t *cds_forward; + ctf_id_t *cds_reverse; + ctf_diff_type_f cds_func; + ctf_diff_guess_t *cds_guess; + void *cds_arg; + uint_t cds_nifuncs; + uint_t cds_nofuncs; + uint_t cds_nextifunc; + uint_t cds_nextofunc; + ctf_diff_func_t *cds_ifuncs; + ctf_diff_func_t *cds_ofuncs; + boolean_t cds_ffillip; + boolean_t cds_fvalid; + uint_t cds_niobj; + uint_t cds_noobj; + uint_t cds_nextiobj; + uint_t cds_nextoobj; + ctf_diff_obj_t *cds_iobj; + ctf_diff_obj_t *cds_oobj; + boolean_t cds_ofillip; + boolean_t cds_ovalid; +}; + +#define TINDEX(tid) (tid - 1) + +/* + * Team Diff + */ +static int ctf_diff_type(ctf_diff_t *, ctf_file_t *, ctf_id_t, ctf_file_t *, + ctf_id_t); + +static int +ctf_diff_name(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + const char *iname, *oname; + const ctf_type_t *itp, *otp; + + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(ifp, iid)); + + iname = ctf_strptr(ifp, itp->ctt_name); + oname = ctf_strptr(ofp, otp->ctt_name); + + if ((iname == NULL || oname == NULL) && (iname != oname)) + return (B_TRUE); + + /* Two anonymous names are the same */ + if (iname == NULL && oname == NULL) + return (B_FALSE); + + return (strcmp(iname, oname) == 0 ? B_FALSE: B_TRUE); +} + +/* + * For floats and ints + */ +static int +ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + ctf_encoding_t ien, den; + + if (ctf_type_encoding(ifp, iid, &ien) != 0) + return (CTF_ERR); + + if (ctf_type_encoding(ofp, oid, &den) != 0) + return (ctf_set_errno(ifp, iid)); + + if (bcmp(&ien, &den, sizeof (ctf_encoding_t)) != 0) + return (B_TRUE); + + return (B_FALSE); +} + +/* + * Two qualifiers are equivalent iff they point to two equivalent types. + */ +static int +ctf_diff_qualifier(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, + ctf_file_t *ofp, ctf_id_t oid) +{ + ctf_id_t iref, oref; + + iref = ctf_type_reference(ifp, iid); + if (iref == CTF_ERR) + return (CTF_ERR); + + oref = ctf_type_reference(ofp, oid); + if (oref == CTF_ERR) + return (ctf_set_errno(ifp, ctf_errno(ofp))); + + return (ctf_diff_type(cds, ifp, iref, ofp, oref)); +} + +/* + * Two arrays are the same iff they have the same type for contents, the same + * type for the index, and the same number of elements. + */ +static int +ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + int ret; + ctf_arinfo_t iar, oar; + + if (ctf_array_info(ifp, iid, &iar) == CTF_ERR) + return (CTF_ERR); + + if (ctf_array_info(ofp, oid, &oar) == CTF_ERR) + return (ctf_set_errno(ifp, ctf_errno(ofp))); + + ret = ctf_diff_type(cds, ifp, iar.ctr_contents, ofp, oar.ctr_contents); + if (ret != B_FALSE) + return (ret); + + if (iar.ctr_nelems != oar.ctr_nelems) + return (B_TRUE); + + /* + * If we're ignoring integer types names, then we're trying to do a bit + * of a logical diff and we don't really care about the fact that the + * index element might not be the same here, what we care about are the + * number of elements and that they're the same type. + */ + if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) { + ret = ctf_diff_type(cds, ifp, iar.ctr_index, ofp, + oar.ctr_index); + if (ret != B_FALSE) + return (ret); + } + + return (B_FALSE); +} + +/* + * Two function pointers are the same if the following is all true: + * + * o They have the same return type + * o They have the same number of arguments + * o The arguments are of the same type + * o They have the same flags + */ +static int +ctf_diff_fptr(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + int ret, i; + ctf_funcinfo_t ifunc, ofunc; + ctf_id_t *iids, *oids; + + if (ctf_func_info_by_id(ifp, iid, &ifunc) == CTF_ERR) + return (CTF_ERR); + + if (ctf_func_info_by_id(ofp, oid, &ofunc) == CTF_ERR) + return (ctf_set_errno(ifp, ctf_errno(ofp))); + + if (ifunc.ctc_argc != ofunc.ctc_argc) + return (B_TRUE); + + if (ifunc.ctc_flags != ofunc.ctc_flags) + return (B_TRUE); + + ret = ctf_diff_type(cds, ifp, ifunc.ctc_return, ofp, ofunc.ctc_return); + if (ret != B_FALSE) + return (ret); + + iids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc); + if (iids == NULL) + return (ctf_set_errno(ifp, ENOMEM)); + + oids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc); + if (oids == NULL) { + ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc); + return (ctf_set_errno(ifp, ENOMEM)); + } + + if (ctf_func_args_by_id(ifp, iid, ifunc.ctc_argc, iids) == CTF_ERR) { + ret = CTF_ERR; + goto out; + } + + if (ctf_func_args_by_id(ofp, oid, ofunc.ctc_argc, oids) == CTF_ERR) { + ret = ctf_set_errno(ifp, ctf_errno(ofp)); + goto out; + } + + ret = B_TRUE; + for (i = 0; i < ifunc.ctc_argc; i++) { + ret = ctf_diff_type(cds, ifp, iids[i], ofp, oids[i]); + if (ret != B_FALSE) + goto out; + } + ret = B_FALSE; + +out: + ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc); + ctf_free(oids, sizeof (ctf_id_t) * ofunc.ctc_argc); + return (ret); +} + +/* + * Two structures are the same if every member is identical to its corresponding + * type, at the same offset, and has the same name. + */ +static int +ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + ctf_file_t *oifp; + const ctf_type_t *itp, *otp; + ssize_t isize, iincr, osize, oincr; + const ctf_member_t *imp, *omp; + const ctf_lmember_t *ilmp, *olmp; + int n; + ctf_diff_guess_t *cdg; + + oifp = ifp; + + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(oifp, ctf_errno(ofp))); + + if (LCTF_INFO_VLEN(ifp, itp->ctt_info) != + LCTF_INFO_VLEN(ofp, otp->ctt_info)) + return (B_TRUE); + + (void) ctf_get_ctt_size(ifp, itp, &isize, &iincr); + (void) ctf_get_ctt_size(ofp, otp, &osize, &oincr); + + if (ifp->ctf_version == CTF_VERSION_1 || isize < CTF_LSTRUCT_THRESH) { + imp = (const ctf_member_t *)((uintptr_t)itp + iincr); + ilmp = NULL; + } else { + imp = NULL; + ilmp = (const ctf_lmember_t *)((uintptr_t)itp + iincr); + } + + if (ofp->ctf_version == CTF_VERSION_1 || osize < CTF_LSTRUCT_THRESH) { + omp = (const ctf_member_t *)((uintptr_t)otp + oincr); + olmp = NULL; + } else { + omp = NULL; + olmp = (const ctf_lmember_t *)((uintptr_t)otp + oincr); + } + + /* + * Insert our assumption that they're equal for the moment. + */ + cdg = ctf_alloc(sizeof (ctf_diff_guess_t)); + if (cdg == NULL) + return (ctf_set_errno(ifp, ENOMEM)); + cdg->cdg_iid = iid; + cdg->cdg_oid = oid; + cdg->cdg_next = cds->cds_guess; + cds->cds_guess = cdg; + cds->cds_forward[TINDEX(iid)] = oid; + cds->cds_reverse[TINDEX(oid)] = iid; + + for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; n--) { + const char *iname, *oname; + ulong_t ioff, ooff; + ctf_id_t itype, otype; + int ret; + + if (imp != NULL) { + iname = ctf_strptr(ifp, imp->ctm_name); + ioff = imp->ctm_offset; + itype = imp->ctm_type; + } else { + iname = ctf_strptr(ifp, ilmp->ctlm_name); + ioff = CTF_LMEM_OFFSET(ilmp); + itype = ilmp->ctlm_type; + } + + if (omp != NULL) { + oname = ctf_strptr(ofp, omp->ctm_name); + ooff = omp->ctm_offset; + otype = omp->ctm_type; + } else { + oname = ctf_strptr(ofp, olmp->ctlm_name); + ooff = CTF_LMEM_OFFSET(olmp); + otype = olmp->ctlm_type; + } + + if (ioff != ooff) { + return (B_TRUE); + } + if (strcmp(iname, oname) != 0) { + return (B_TRUE); + } + ret = ctf_diff_type(cds, ifp, itype, ofp, otype); + if (ret != B_FALSE) { + return (ret); + } + + /* Advance our pointers */ + if (imp != NULL) + imp++; + if (ilmp != NULL) + ilmp++; + if (omp != NULL) + omp++; + if (olmp != NULL) + olmp++; + } + + return (B_FALSE); +} + +/* + * Two unions are the same if they have the same set of members. This is similar + * to, but slightly different from a struct. The offsets of members don't + * matter. However, their is no guarantee of ordering so we have to fall back to + * doing an O(N^2) scan. + */ +typedef struct ctf_diff_union_member { + ctf_diff_t *cdum_cds; + ctf_file_t *cdum_fp; + ctf_file_t *cdum_iterfp; + const char *cdum_name; + ctf_id_t cdum_type; + int cdum_ret; +} ctf_diff_union_member_t; + +typedef struct ctf_diff_union_fp { + ctf_diff_t *cduf_cds; + ctf_file_t *cduf_curfp; + ctf_file_t *cduf_altfp; + ctf_id_t cduf_type; + int cduf_ret; +} ctf_diff_union_fp_t; + +/* ARGSUSED */ +static int +ctf_diff_union_check_member(const char *name, ctf_id_t id, ulong_t off, + void *arg) +{ + int ret; + ctf_diff_union_member_t *cdump = arg; + + if (strcmp(name, cdump->cdum_name) != 0) + return (0); + + ret = ctf_diff_type(cdump->cdum_cds, cdump->cdum_fp, cdump->cdum_type, + cdump->cdum_iterfp, id); + if (ret == CTF_ERR) { + cdump->cdum_ret = CTF_ERR; + return (1); + } + + if (ret == B_FALSE) { + cdump->cdum_ret = B_FALSE; + /* Return non-zero to stop iteration as we have a match */ + return (1); + } + + return (0); +} + +/* ARGSUSED */ +static int +ctf_diff_union_check_fp(const char *name, ctf_id_t id, ulong_t off, void *arg) +{ + int ret; + ctf_diff_union_member_t cdum; + ctf_diff_union_fp_t *cdufp = arg; + + cdum.cdum_cds = cdufp->cduf_cds; + cdum.cdum_fp = cdufp->cduf_curfp; + cdum.cdum_iterfp = cdufp->cduf_altfp; + cdum.cdum_name = name; + cdum.cdum_type = id; + cdum.cdum_ret = B_TRUE; + + ret = ctf_member_iter(cdum.cdum_iterfp, cdufp->cduf_type, + ctf_diff_union_check_member, &cdum); + if (ret == 0 || cdum.cdum_ret == CTF_ERR) { + /* No match found or error, terminate now */ + cdufp->cduf_ret = cdum.cdum_ret; + return (1); + } else if (ret == CTF_ERR) { + (void) ctf_set_errno(cdum.cdum_fp, ctf_errno(cdum.cdum_iterfp)); + cdufp->cduf_ret = CTF_ERR; + return (1); + } else { + ASSERT(cdum.cdum_ret == B_FALSE); + cdufp->cduf_ret = cdum.cdum_ret; + return (0); + } +} + +static int +ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + ctf_file_t *oifp; + const ctf_type_t *itp, *otp; + ctf_diff_union_fp_t cduf; + ctf_diff_guess_t *cdg; + int ret; + + oifp = ifp; + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(oifp, ctf_errno(ofp))); + + if (LCTF_INFO_VLEN(ifp, itp->ctt_info) != + LCTF_INFO_VLEN(ofp, otp->ctt_info)) + return (B_TRUE); + + cdg = ctf_alloc(sizeof (ctf_diff_guess_t)); + if (cdg == NULL) + return (ctf_set_errno(ifp, ENOMEM)); + cdg->cdg_iid = iid; + cdg->cdg_oid = oid; + cdg->cdg_next = cds->cds_guess; + cds->cds_guess = cdg; + cds->cds_forward[TINDEX(iid)] = oid; + cds->cds_reverse[TINDEX(oid)] = iid; + + cduf.cduf_cds = cds; + cduf.cduf_curfp = ifp; + cduf.cduf_altfp = ofp; + cduf.cduf_type = oid; + cduf.cduf_ret = B_TRUE; + ret = ctf_member_iter(ifp, iid, ctf_diff_union_check_fp, &cduf); + if (ret != CTF_ERR) + ret = cduf.cduf_ret; + + return (ret); +} + +/* + * Two enums are equivalent if they share the same underlying type and they have + * the same set of members. + */ +static int +ctf_diff_enum(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + ctf_file_t *oifp; + const ctf_type_t *itp, *otp; + ssize_t iincr, oincr; + const ctf_enum_t *iep, *oep; + int n; + + oifp = ifp; + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(oifp, ctf_errno(ofp))); + + if (LCTF_INFO_VLEN(ifp, itp->ctt_info) != + LCTF_INFO_VLEN(ofp, otp->ctt_info)) + return (B_TRUE); + + (void) ctf_get_ctt_size(ifp, itp, NULL, &iincr); + (void) ctf_get_ctt_size(ofp, otp, NULL, &oincr); + iep = (const ctf_enum_t *)((uintptr_t)itp + iincr); + oep = (const ctf_enum_t *)((uintptr_t)otp + oincr); + + for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; + n--, iep++, oep++) { + if (strcmp(ctf_strptr(ifp, iep->cte_name), + ctf_strptr(ofp, oep->cte_name)) != 0) + return (B_TRUE); + + if (iep->cte_value != oep->cte_value) + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * Two forwards are equivalent in one of two cases. If both are forwards, than + * they are the same. Otherwise, they're equivalent if one is a struct or union + * and the other is a forward. + */ +static int +ctf_diff_forward(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + int ikind, okind; + + ikind = ctf_type_kind(ifp, iid); + okind = ctf_type_kind(ofp, oid); + + if (ikind == okind) { + ASSERT(ikind == CTF_K_FORWARD); + return (B_FALSE); + } else if (ikind == CTF_K_FORWARD) { + return (okind != CTF_K_UNION && okind != CTF_K_STRUCT); + } else { + return (ikind != CTF_K_UNION && ikind != CTF_K_STRUCT); + } +} + +/* + * Are two types equivalent? + */ +int +ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + int ret, ikind, okind; + + /* Do a quick short circuit */ + if (ifp == ofp && iid == oid) + return (B_FALSE); + + /* + * Check if it's something we've already encountered in a forward + * reference or forward negative table. Also double check the reverse + * table. + */ + if (cds->cds_forward[TINDEX(iid)] == oid) + return (B_FALSE); + if (cds->cds_forward[TINDEX(iid)] != 0) + return (B_TRUE); + if (cds->cds_reverse[TINDEX(oid)] == iid) + return (B_FALSE); + if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0 && + cds->cds_reverse[TINDEX(oid)] != 0) + return (B_TRUE); + + ikind = ctf_type_kind(ifp, iid); + okind = ctf_type_kind(ofp, oid); + + if (ikind != okind && + ikind != CTF_K_FORWARD && okind != CTF_K_FORWARD) + return (B_TRUE); + + /* Check names */ + if ((ret = ctf_diff_name(ifp, iid, ofp, oid)) != B_FALSE) { + if (ikind != okind || ikind != CTF_K_INTEGER || + (cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) + return (ret); + } + + if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD) + return (ctf_diff_forward(ifp, iid, ofp, oid)); + + switch (ikind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + ret = ctf_diff_number(ifp, iid, ofp, oid); + break; + case CTF_K_ARRAY: + ret = ctf_diff_array(cds, ifp, iid, ofp, oid); + break; + case CTF_K_FUNCTION: + ret = ctf_diff_fptr(cds, ifp, iid, ofp, oid); + break; + case CTF_K_STRUCT: + ret = ctf_diff_struct(cds, ifp, iid, ofp, oid); + break; + case CTF_K_UNION: + ret = ctf_diff_union(cds, ifp, iid, ofp, oid); + break; + case CTF_K_ENUM: + ret = ctf_diff_enum(ifp, iid, ofp, oid); + break; + case CTF_K_FORWARD: + ret = ctf_diff_forward(ifp, iid, ofp, oid); + break; + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ret = ctf_diff_qualifier(cds, ifp, iid, ofp, oid); + break; + case CTF_K_UNKNOWN: + /* + * The current CTF tools use CTF_K_UNKNOWN as a padding type. We + * always declare two instances of CTF_K_UNKNOWN as different, + * even though this leads to additional diff noise. + */ + ret = B_TRUE; + break; + default: + abort(); + } + + return (ret); +} + +/* + * Walk every type in the first container and try to find a match in the second. + * If there is a match, then update both the forward and reverse mapping tables. + */ +static int +ctf_diff_pass1(ctf_diff_t *cds) +{ + int i, j, diff; + int istart, iend, jstart, jend; + + if (cds->cds_ifp->ctf_flags & LCTF_CHILD) { + istart = 0x8001; + iend = cds->cds_ifp->ctf_typemax + 0x8000; + } else { + istart = 1; + iend = cds->cds_ifp->ctf_typemax; + } + + if (cds->cds_ofp->ctf_flags & LCTF_CHILD) { + jstart = 0x8001; + jend = cds->cds_ofp->ctf_typemax + 0x8000; + } else { + jstart = 1; + jend = cds->cds_ofp->ctf_typemax; + } + + for (i = istart; i <= iend; i++) { + diff = B_TRUE; + for (j = jstart; j <= jend; j++) { + ctf_diff_guess_t *cdg, *tofree; + + ASSERT(cds->cds_guess == NULL); + diff = ctf_diff_type(cds, cds->cds_ifp, i, + cds->cds_ofp, j); + if (diff == CTF_ERR) + return (CTF_ERR); + + /* Clean up our guesses */ + cdg = cds->cds_guess; + cds->cds_guess = NULL; + while (cdg != NULL) { + if (diff == B_TRUE) { + cds->cds_forward[TINDEX(cdg->cdg_iid)] = + 0; + cds->cds_reverse[TINDEX(cdg->cdg_oid)] = + 0; + } + tofree = cdg; + cdg = cdg->cdg_next; + ctf_free(tofree, sizeof (ctf_diff_guess_t)); + } + + /* Found a hit, update the tables */ + if (diff == B_FALSE) { + cds->cds_forward[TINDEX(i)] = j; + if (cds->cds_reverse[TINDEX(j)] == 0) + cds->cds_reverse[TINDEX(j)] = i; + break; + } + } + + /* Call the callback at this point */ + if (diff == B_TRUE) { + cds->cds_forward[TINDEX(i)] = CTF_ERR; + cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR, + cds->cds_arg); + } else { + cds->cds_func(cds->cds_ifp, i, B_TRUE, cds->cds_ofp, j, + cds->cds_arg); + } + } + + return (0); +} + +/* + * Now we need to walk the second container and emit anything that we didn't + * find as common in the first pass. + */ +static int +ctf_diff_pass2(ctf_diff_t *cds) +{ + int i, start, end; + + start = 0x1; + end = cds->cds_ofp->ctf_typemax; + if (cds->cds_ofp->ctf_flags & LCTF_CHILD) { + start += 0x8000; + end += 0x8000; + } + + for (i = start; i <= end; i++) { + if (cds->cds_reverse[TINDEX(i)] != 0) + continue; + cds->cds_func(cds->cds_ofp, i, B_FALSE, NULL, CTF_ERR, + cds->cds_arg); + } + + return (0); +} + +int +ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp) +{ + ctf_diff_t *cds; + size_t fsize, rsize; + + cds = ctf_alloc(sizeof (ctf_diff_t)); + if (cds == NULL) + return (ctf_set_errno(ifp, ENOMEM)); + + bzero(cds, sizeof (ctf_diff_t)); + cds->cds_ifp = ifp; + cds->cds_ofp = ofp; + + fsize = sizeof (ctf_id_t) * ifp->ctf_typemax; + rsize = sizeof (ctf_id_t) * ofp->ctf_typemax; + if (ifp->ctf_flags & LCTF_CHILD) + fsize += 0x8000 * sizeof (ctf_id_t); + if (ofp->ctf_flags & LCTF_CHILD) + rsize += 0x8000 * sizeof (ctf_id_t); + + cds->cds_forward = ctf_alloc(fsize); + if (cds->cds_forward == NULL) { + ctf_free(cds, sizeof (ctf_diff_t)); + return (ctf_set_errno(ifp, ENOMEM)); + } + cds->cds_reverse = ctf_alloc(rsize); + if (cds->cds_reverse == NULL) { + ctf_free(cds->cds_forward, fsize); + ctf_free(cds, sizeof (ctf_diff_t)); + return (ctf_set_errno(ifp, ENOMEM)); + } + bzero(cds->cds_forward, fsize); + bzero(cds->cds_reverse, rsize); + + cds->cds_ifp->ctf_refcnt++; + cds->cds_ofp->ctf_refcnt++; + *cdsp = cds; + return (0); +} + +int +ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg) +{ + int ret; + + cds->cds_func = cb; + cds->cds_arg = arg; + + ret = ctf_diff_pass1(cds); + if (ret == 0) + ret = ctf_diff_pass2(cds); + + cds->cds_func = NULL; + cds->cds_arg = NULL; + cds->cds_tvalid = B_TRUE; + return (ret); +} + +void +ctf_diff_fini(ctf_diff_t *cds) +{ + ctf_diff_guess_t *cdg; + size_t fsize, rsize; + + cds->cds_ifp->ctf_refcnt--; + cds->cds_ofp->ctf_refcnt--; + + fsize = sizeof (ctf_id_t) * cds->cds_ifp->ctf_typemax; + rsize = sizeof (ctf_id_t) * cds->cds_ofp->ctf_typemax; + if (cds->cds_ifp->ctf_flags & LCTF_CHILD) + fsize += 0x8000 * sizeof (ctf_id_t); + if (cds->cds_ofp->ctf_flags & LCTF_CHILD) + rsize += 0x8000 * sizeof (ctf_id_t); + + if (cds->cds_ifuncs != NULL) + ctf_free(cds->cds_ifuncs, + sizeof (ctf_diff_func_t) * cds->cds_nifuncs); + if (cds->cds_ofuncs != NULL) + ctf_free(cds->cds_ofuncs, + sizeof (ctf_diff_func_t) * cds->cds_nofuncs); + if (cds->cds_iobj != NULL) + ctf_free(cds->cds_iobj, + sizeof (ctf_diff_obj_t) * cds->cds_niobj); + if (cds->cds_oobj != NULL) + ctf_free(cds->cds_oobj, + sizeof (ctf_diff_obj_t) * cds->cds_noobj); + cdg = cds->cds_guess; + while (cdg != NULL) { + ctf_diff_guess_t *tofree = cdg; + cdg = cdg->cdg_next; + ctf_free(tofree, sizeof (ctf_diff_guess_t)); + } + ctf_free(cds, sizeof (ctf_diff_t)); +} + +uint_t +ctf_diff_getflags(ctf_diff_t *cds) +{ + return (cds->cds_flags); +} + +int +ctf_diff_setflags(ctf_diff_t *cds, uint_t flags) +{ + if ((flags & ~CTF_DIFF_F_MASK) != 0) + return (ctf_set_errno(cds->cds_ifp, EINVAL)); + + cds->cds_flags = flags; + return (0); +} + +static boolean_t +ctf_diff_symid(ctf_diff_t *cds, ctf_id_t iid, ctf_id_t oid) +{ + ctf_file_t *ifp, *ofp; + + ifp = cds->cds_ifp; + ofp = cds->cds_ofp; + + /* + * If we have parent containers on the scene here, we need to go through + * and do a full diff check because while a diff for types will not + * actually go through and check types in the parent container. + */ + if (iid == 0 || oid == 0) + return (iid == oid ? B_FALSE: B_TRUE); + + if (!(ifp->ctf_flags & LCTF_CHILD) && !(ofp->ctf_flags & LCTF_CHILD)) { + if (cds->cds_forward[TINDEX(iid)] != oid) + return (B_TRUE); + return (B_FALSE); + } + + return (ctf_diff_type(cds, ifp, iid, ofp, oid)); +} + +/* ARGSUSED */ +static void +ctf_diff_void_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, + ctf_id_t oid, void *arg) +{ +} + +/* ARGSUSED */ +static int +ctf_diff_func_count(const char *name, ulong_t symidx, ctf_funcinfo_t *fip, + void *arg) +{ + uint32_t *ip = arg; + + *ip = *ip + 1; + return (0); +} + +/* ARGSUSED */ +static int +ctf_diff_func_fill_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *fip, + void *arg) +{ + uint_t *next, max; + ctf_diff_func_t *funcptr; + ctf_diff_t *cds = arg; + + if (cds->cds_ffillip == B_TRUE) { + max = cds->cds_nifuncs; + next = &cds->cds_nextifunc; + funcptr = cds->cds_ifuncs + *next; + } else { + max = cds->cds_nofuncs; + next = &cds->cds_nextofunc; + funcptr = cds->cds_ofuncs + *next; + + } + + VERIFY(*next < max); + funcptr->cdf_name = name; + funcptr->cdf_symidx = symidx; + funcptr->cdf_matchidx = ULONG_MAX; + *next = *next + 1; + + return (0); +} + +int +ctf_diff_func_fill(ctf_diff_t *cds) +{ + int ret; + uint32_t ifcount, ofcount, idcnt, cti; + ulong_t i, j; + ctf_id_t *iids, *oids; + + ifcount = 0; + ofcount = 0; + idcnt = 0; + iids = NULL; + oids = NULL; + + ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_count, &ifcount); + if (ret != 0) + return (ret); + ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_count, &ofcount); + if (ret != 0) + return (ret); + + cds->cds_ifuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ifcount); + if (cds->cds_ifuncs == NULL) + return (ctf_set_errno(cds->cds_ifp, ENOMEM)); + + cds->cds_nifuncs = ifcount; + cds->cds_nextifunc = 0; + + cds->cds_ofuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ofcount); + if (cds->cds_ofuncs == NULL) + return (ctf_set_errno(cds->cds_ifp, ENOMEM)); + + cds->cds_nofuncs = ofcount; + cds->cds_nextofunc = 0; + + cds->cds_ffillip = B_TRUE; + if ((ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_fill_cb, + cds)) != 0) + return (ret); + + cds->cds_ffillip = B_FALSE; + if ((ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_fill_cb, + cds)) != 0) + return (ret); + + /* + * Everything is initialized to not match. This could probably be faster + * with something that used a hash. But this part of the diff isn't used + * by merge. + */ + for (i = 0; i < cds->cds_nifuncs; i++) { + for (j = 0; j < cds->cds_nofuncs; j++) { + ctf_diff_func_t *ifd, *ofd; + ctf_funcinfo_t ifip, ofip; + boolean_t match; + + ifd = &cds->cds_ifuncs[i]; + ofd = &cds->cds_ofuncs[j]; + if (strcmp(ifd->cdf_name, ofd->cdf_name) != 0) + continue; + + ret = ctf_func_info(cds->cds_ifp, ifd->cdf_symidx, + &ifip); + if (ret != 0) + goto out; + ret = ctf_func_info(cds->cds_ofp, ofd->cdf_symidx, + &ofip); + if (ret != 0) { + ret = ctf_set_errno(cds->cds_ifp, + ctf_errno(cds->cds_ofp)); + goto out; + } + + if (ifip.ctc_argc != ofip.ctc_argc && + ifip.ctc_flags != ofip.ctc_flags) + continue; + + /* Validate return type and arguments are the same */ + if (ctf_diff_symid(cds, ifip.ctc_return, + ofip.ctc_return)) + continue; + + if (ifip.ctc_argc > idcnt) { + if (iids != NULL) + ctf_free(iids, + sizeof (ctf_id_t) * idcnt); + if (oids != NULL) + ctf_free(oids, + sizeof (ctf_id_t) * idcnt); + iids = oids = NULL; + idcnt = ifip.ctc_argc; + iids = ctf_alloc(sizeof (ctf_id_t) * idcnt); + if (iids == NULL) { + ret = ctf_set_errno(cds->cds_ifp, + ENOMEM); + goto out; + } + oids = ctf_alloc(sizeof (ctf_id_t) * idcnt); + if (iids == NULL) { + ret = ctf_set_errno(cds->cds_ifp, + ENOMEM); + goto out; + } + } + + if ((ret = ctf_func_args(cds->cds_ifp, ifd->cdf_symidx, + ifip.ctc_argc, iids)) != 0) + goto out; + if ((ret = ctf_func_args(cds->cds_ofp, ofd->cdf_symidx, + ofip.ctc_argc, oids)) != 0) + goto out; + + match = B_TRUE; + for (cti = 0; cti < ifip.ctc_argc; cti++) { + if (ctf_diff_symid(cds, iids[cti], oids[cti])) { + match = B_FALSE; + break; + } + } + + if (match == B_FALSE) + continue; + + ifd->cdf_matchidx = j; + ofd->cdf_matchidx = i; + break; + } + } + + ret = 0; + +out: + if (iids != NULL) + ctf_free(iids, sizeof (ctf_id_t) * idcnt); + if (oids != NULL) + ctf_free(oids, sizeof (ctf_id_t) * idcnt); + + return (ret); +} + +/* + * In general, two functions are the same, if they have the same name and their + * arguments have the same types, including the return type. Like types, we + * basically have to do this in two passes. In the first phase we walk every + * type in the first container and try to find a match in the second. + */ +int +ctf_diff_functions(ctf_diff_t *cds, ctf_diff_func_f cb, void *arg) +{ + int ret; + ulong_t i; + + if (cds->cds_tvalid == B_FALSE) { + if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0) + return (ret); + } + + if (cds->cds_fvalid == B_FALSE) { + if ((ret = ctf_diff_func_fill(cds)) != 0) + return (ret); + cds->cds_fvalid = B_TRUE; + } + + for (i = 0; i < cds->cds_nifuncs; i++) { + if (cds->cds_ifuncs[i].cdf_matchidx == ULONG_MAX) { + cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, + B_FALSE, NULL, ULONG_MAX, arg); + } else { + ulong_t idx = cds->cds_ifuncs[i].cdf_matchidx; + cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, B_TRUE, + cds->cds_ofp, cds->cds_ofuncs[idx].cdf_symidx, arg); + } + } + + for (i = 0; i < cds->cds_nofuncs; i++) { + if (cds->cds_ofuncs[i].cdf_matchidx != ULONG_MAX) + continue; + cb(cds->cds_ofp, cds->cds_ofuncs[i].cdf_symidx, B_FALSE, + NULL, ULONG_MAX, arg); + } + + return (0); +} + +static int +ctf_diff_obj_fill_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg) +{ + uint_t *next, max; + ctf_diff_obj_t *objptr; + ctf_diff_t *cds = arg; + + if (cds->cds_ofillip == B_TRUE) { + max = cds->cds_niobj; + next = &cds->cds_nextiobj; + objptr = cds->cds_iobj + *next; + } else { + max = cds->cds_noobj; + next = &cds->cds_nextoobj; + objptr = cds->cds_oobj+ *next; + + } + + VERIFY(*next < max); + objptr->cdo_name = name; + objptr->cdo_symidx = symidx; + objptr->cdo_id = id; + objptr->cdo_matchidx = ULONG_MAX; + *next = *next + 1; + + return (0); +} + +/* ARGSUSED */ +static int +ctf_diff_obj_count(const char *name, ctf_id_t id, ulong_t symidx, void *arg) +{ + uint32_t *count = arg; + + *count = *count + 1; + + return (0); +} + + +static int +ctf_diff_obj_fill(ctf_diff_t *cds) +{ + int ret; + uint32_t iocount, oocount; + ulong_t i, j; + + iocount = 0; + oocount = 0; + + ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_count, &iocount); + if (ret != 0) + return (ret); + + ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_count, &oocount); + if (ret != 0) + return (ret); + + cds->cds_iobj = ctf_alloc(sizeof (ctf_diff_obj_t) * iocount); + if (cds->cds_iobj == NULL) + return (ctf_set_errno(cds->cds_ifp, ENOMEM)); + cds->cds_niobj = iocount; + cds->cds_nextiobj = 0; + + cds->cds_oobj = ctf_alloc(sizeof (ctf_diff_obj_t) * oocount); + if (cds->cds_oobj == NULL) + return (ctf_set_errno(cds->cds_ifp, ENOMEM)); + cds->cds_noobj = oocount; + cds->cds_nextoobj = 0; + + cds->cds_ofillip = B_TRUE; + if ((ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_fill_cb, + cds)) != 0) + return (ret); + + cds->cds_ofillip = B_FALSE; + if ((ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_fill_cb, + cds)) != 0) + return (ret); + + for (i = 0; i < cds->cds_niobj; i++) { + for (j = 0; j < cds->cds_noobj; j++) { + ctf_diff_obj_t *id, *od; + + id = &cds->cds_iobj[i]; + od = &cds->cds_oobj[j]; + + if (id->cdo_name == NULL || od->cdo_name == NULL) + continue; + if (strcmp(id->cdo_name, od->cdo_name) != 0) + continue; + + if (ctf_diff_symid(cds, id->cdo_id, od->cdo_id)) { + continue; + } + + id->cdo_matchidx = j; + od->cdo_matchidx = i; + break; + } + } + + return (0); +} + +int +ctf_diff_objects(ctf_diff_t *cds, ctf_diff_obj_f cb, void *arg) +{ + int ret; + ulong_t i; + + if (cds->cds_tvalid == B_FALSE) { + if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0) + return (ret); + } + + if (cds->cds_ovalid == B_FALSE) { + if ((ret = ctf_diff_obj_fill(cds)) != 0) + return (ret); + cds->cds_ovalid = B_TRUE; + } + + for (i = 0; i < cds->cds_niobj; i++) { + ctf_diff_obj_t *o = &cds->cds_iobj[i]; + + if (cds->cds_iobj[i].cdo_matchidx == ULONG_MAX) { + cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_FALSE, + NULL, ULONG_MAX, CTF_ERR, arg); + } else { + ctf_diff_obj_t *alt = &cds->cds_oobj[o->cdo_matchidx]; + cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_TRUE, + cds->cds_ofp, alt->cdo_symidx, alt->cdo_id, arg); + } + } + + for (i = 0; i < cds->cds_noobj; i++) { + ctf_diff_obj_t *o = &cds->cds_oobj[i]; + if (o->cdo_matchidx != ULONG_MAX) + continue; + cb(cds->cds_ofp, o->cdo_symidx, o->cdo_id, B_FALSE, NULL, + ULONG_MAX, CTF_ERR, arg); + } + + return (0); +} diff --git a/usr/src/lib/libctf/common/ctf_elfwrite.c b/usr/src/lib/libctf/common/ctf_elfwrite.c new file mode 100644 index 0000000000..c8a4d22cf7 --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_elfwrite.c @@ -0,0 +1,412 @@ +/* + * 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 (c) 2015, Joyent, Inc. + */ + +/* + * Routines for writing ctf data to elf files, originally from the ctf tools. + */ + +#include <ctf_impl.h> +#include <libctf.h> +#include <gelf.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <libelf.h> +#include <sys/zmod.h> + +static int +ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags) +{ + GElf_Ehdr sehdr, dehdr; + Elf_Scn *sscn, *dscn; + Elf_Data *sdata, *ddata; + GElf_Shdr shdr; + int symtab_idx = -1; + off_t new_offset = 0; + off_t ctfnameoff = 0; + int compress = (flags & CTF_ELFWRITE_F_COMPRESS); + int *secxlate; + int srcidx, dstidx, pad, i; + int curnmoff = 0; + int changing = 0; + size_t nshdr, nphdr, strndx; + + void *cdata = NULL; + + if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) + return (ctf_set_errno(fp, EINVAL)); + + if (gelf_newehdr(dst, gelf_getclass(src)) == NULL) + return (ctf_set_errno(fp, ECTF_ELF)); + + if (gelf_getehdr(src, &sehdr) == NULL) + return (ctf_set_errno(fp, ECTF_ELF)); + (void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); + if (gelf_update_ehdr(dst, &dehdr) == 0) + return (ctf_set_errno(fp, ECTF_ELF)); + + /* + * Use libelf to get the number of sections and the string section to + * deal with ELF files that may have a large number of sections. We just + * always use this to make our live easier. + */ + if (elf_getphdrnum(src, &nphdr) != 0) + return (ctf_set_errno(fp, ECTF_ELF)); + if (elf_getshdrnum(src, &nshdr) != 0) + return (ctf_set_errno(fp, ECTF_ELF)); + if (elf_getshdrstrndx(src, &strndx) != 0) + return (ctf_set_errno(fp, ECTF_ELF)); + + /* + * Neither the existing debug sections nor the SUNW_ctf sections (new or + * existing) are SHF_ALLOC'd, so they won't be in areas referenced by + * program headers. As such, we can just blindly copy the program + * headers from the existing file to the new file. + */ + if (nphdr != 0) { + (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT); + if (gelf_newphdr(dst, nphdr) == NULL) + return (ctf_set_errno(fp, ECTF_ELF)); + + for (i = 0; i < nphdr; i++) { + GElf_Phdr phdr; + + if (gelf_getphdr(src, i, &phdr) == NULL) + return (ctf_set_errno(fp, ECTF_ELF)); + if (gelf_update_phdr(dst, i, &phdr) == 0) + return (ctf_set_errno(fp, ECTF_ELF)); + } + } + + secxlate = ctf_alloc(sizeof (int) * nshdr); + for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) { + Elf_Scn *scn = elf_getscn(src, srcidx); + GElf_Shdr shdr; + char *sname; + + if (gelf_getshdr(scn, &shdr) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + sname = elf_strptr(src, strndx, shdr.sh_name); + if (sname == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) { + secxlate[srcidx] = -1; + } else { + secxlate[srcidx] = dstidx++; + curnmoff += strlen(sname) + 1; + } + + new_offset = (off_t)dehdr.e_phoff; + } + + for (srcidx = 1; srcidx < nshdr; srcidx++) { + char *sname; + + sscn = elf_getscn(src, srcidx); + if (gelf_getshdr(sscn, &shdr) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + if (secxlate[srcidx] == -1) { + changing = 1; + continue; + } + + dscn = elf_newscn(dst); + if (dscn == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + /* + * If this file has program headers, we need to explicitly lay + * out sections. If none of the sections prior to this one have + * been removed, then we can just use the existing location. If + * one or more sections have been changed, then we need to + * adjust this one to avoid holes. + */ + if (changing && nphdr != 0) { + pad = new_offset % shdr.sh_addralign; + + if (pad != 0) + new_offset += shdr.sh_addralign - pad; + shdr.sh_offset = new_offset; + } + + shdr.sh_link = secxlate[shdr.sh_link]; + + if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA) + shdr.sh_info = secxlate[shdr.sh_info]; + + sname = elf_strptr(src, strndx, shdr.sh_name); + if (sname == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + if ((sdata = elf_getdata(sscn, NULL)) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + if ((ddata = elf_newdata(dscn)) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + bcopy(sdata, ddata, sizeof (Elf_Data)); + + if (srcidx == strndx) { + char seclen = strlen(CTF_ELF_SCN_NAME); + + ddata->d_buf = ctf_alloc(ddata->d_size + shdr.sh_size + + seclen + 1); + if (ddata->d_buf == NULL) { + ctf_free(secxlate, + sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); + (void) strcpy((caddr_t)ddata->d_buf + shdr.sh_size, + CTF_ELF_SCN_NAME); + ctfnameoff = (off_t)shdr.sh_size; + shdr.sh_size += seclen + 1; + ddata->d_size += seclen + 1; + + if (nphdr != 0) + changing = 1; + } + + if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { + int nsym = shdr.sh_size / shdr.sh_entsize; + + symtab_idx = secxlate[srcidx]; + + ddata->d_buf = ctf_alloc(shdr.sh_size); + if (ddata->d_buf == NULL) { + ctf_free(secxlate, + sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + (void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); + + for (i = 0; i < nsym; i++) { + GElf_Sym sym; + short newscn; + + (void) gelf_getsym(ddata, i, &sym); + + if (sym.st_shndx >= SHN_LORESERVE) + continue; + + if ((newscn = secxlate[sym.st_shndx]) != + sym.st_shndx) { + sym.st_shndx = + (newscn == -1 ? 1 : newscn); + + if (gelf_update_sym(ddata, i, &sym) == + 0) { + ctf_free(secxlate, + sizeof (int) * + nshdr); + return (ctf_set_errno(fp, + ECTF_ELF)); + } + } + } + } + + if (gelf_update_shdr(dscn, &shdr) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + new_offset = (off_t)shdr.sh_offset; + if (shdr.sh_type != SHT_NOBITS) + new_offset += shdr.sh_size; + } + + if (symtab_idx == -1) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + /* Add the ctf section */ + if ((dscn = elf_newscn(dst)) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + if (gelf_getshdr(dscn, &shdr) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + shdr.sh_name = ctfnameoff; + shdr.sh_type = SHT_PROGBITS; + shdr.sh_size = fp->ctf_size; + shdr.sh_link = symtab_idx; + shdr.sh_addralign = 4; + if (changing && nphdr != 0) { + pad = new_offset % shdr.sh_addralign; + + if (pad) + new_offset += shdr.sh_addralign - pad; + + shdr.sh_offset = new_offset; + new_offset += shdr.sh_size; + } + + if ((ddata = elf_newdata(dscn)) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + if (compress != 0) { + size_t dlen; + ctf_header_t *cthp; + int err; + + if (ctf_zopen(&err) == NULL) { + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, err)); + } + + dlen = fp->ctf_size; + cdata = ctf_data_alloc(dlen); + bcopy(fp->ctf_base, cdata, sizeof (ctf_header_t)); + cthp = cdata; + cthp->cth_flags |= CTF_F_COMPRESS; + dlen -= sizeof (ctf_header_t); + if (z_compress((void *)((uintptr_t)cdata + + sizeof (ctf_header_t)), &dlen, + fp->ctf_base + sizeof (ctf_header_t), + fp->ctf_size - sizeof (ctf_header_t)) != Z_OK) { + ctf_data_free(cdata, fp->ctf_size); + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ZLIB)); + } + ddata->d_buf = cdata; + ddata->d_size = dlen + sizeof (ctf_header_t); + } else { + ddata->d_buf = (void *)fp->ctf_base; + ddata->d_size = fp->ctf_size; + } + ddata->d_align = shdr.sh_addralign; + + if (gelf_update_shdr(dscn, &shdr) == 0) { + if (cdata != NULL) + ctf_data_free(cdata, fp->ctf_size); + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + /* update the section header location */ + if (nphdr != 0) { + size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT); + size_t r = new_offset % align; + + if (r) + new_offset += align - r; + + dehdr.e_shoff = new_offset; + } + + /* commit to disk */ + if (sehdr.e_shstrndx == SHN_XINDEX) + dehdr.e_shstrndx = SHN_XINDEX; + else + dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx]; + if (gelf_update_ehdr(dst, &dehdr) == NULL) { + if (cdata != NULL) + ctf_data_free(cdata, fp->ctf_size); + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + if (elf_update(dst, ELF_C_WRITE) < 0) { + if (cdata != NULL) + ctf_data_free(cdata, fp->ctf_size); + ctf_free(secxlate, sizeof (int) * nshdr); + return (ctf_set_errno(fp, ECTF_ELF)); + } + + if (cdata != NULL) + ctf_data_free(cdata, fp->ctf_size); + ctf_free(secxlate, sizeof (int) * nshdr); + + return (0); +} + +int +ctf_elffdwrite(ctf_file_t *fp, int ifd, int ofd, int flags) +{ + int ret; + Elf *ielf, *oelf; + + (void) elf_version(EV_CURRENT); + if ((ielf = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) + return (ctf_set_errno(fp, ECTF_ELF)); + + if ((oelf = elf_begin(ofd, ELF_C_WRITE, NULL)) == NULL) + return (ctf_set_errno(fp, ECTF_ELF)); + + ret = ctf_write_elf(fp, ielf, oelf, flags); + + (void) elf_end(ielf); + (void) elf_end(oelf); + + return (ret); +} + +int +ctf_elfwrite(ctf_file_t *fp, const char *input, const char *output, int flags) +{ + struct stat st; + int ifd, ofd, ret; + + if ((ifd = open(input, O_RDONLY)) < 0) + return (ctf_set_errno(fp, errno)); + + if (fstat(ifd, &st) < 0) + return (ctf_set_errno(fp, errno)); + + if ((ofd = open(output, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) + return (ctf_set_errno(fp, errno)); + + ret = ctf_elffdwrite(fp, ifd, ofd, flags); + + if (close(ifd) != 0 && ret != 0) + ret = ctf_set_errno(fp, errno); + if (close(ofd) != 0 && ret != 0) + ret = ctf_set_errno(fp, errno); + + return (ret); +} diff --git a/usr/src/lib/libctf/common/ctf_lib.c b/usr/src/lib/libctf/common/ctf_lib.c index e71ebc6d9d..4658fc8ddc 100644 --- a/usr/src/lib/libctf/common/ctf_lib.c +++ b/usr/src/lib/libctf/common/ctf_lib.c @@ -23,6 +23,9 @@ * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2015, Joyent, Inc. + */ #include <sys/types.h> #include <sys/stat.h> @@ -33,6 +36,8 @@ #include <errno.h> #include <dlfcn.h> #include <gelf.h> +#include <zlib.h> +#include <zone.h> #ifdef _LP64 static const char *_libctf_zlib = "/usr/lib/64/libz.so.1"; @@ -42,6 +47,9 @@ static const char *_libctf_zlib = "/usr/lib/libz.so.1"; static struct { int (*z_uncompress)(uchar_t *, ulong_t *, const uchar_t *, ulong_t); + int (*z_initcomp)(z_stream *, int, const char *, int); + int (*z_compress)(z_stream *, int); + int (*z_finicomp)(z_stream *); const char *(*z_error)(int); void *z_dlp; } zlib; @@ -72,21 +80,47 @@ _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"); + zlib.z_initcomp = (int (*)()) dlsym(zlib.z_dlp, "deflateInit_"); + zlib.z_compress = (int (*)()) dlsym(zlib.z_dlp, "deflate"); + zlib.z_finicomp = (int (*)()) dlsym(zlib.z_dlp, "deflateEnd"); zlib.z_error = (const char *(*)()) dlsym(zlib.z_dlp, "zError"); - if (zlib.z_uncompress == NULL || zlib.z_error == NULL) { + if (zlib.z_uncompress == NULL || zlib.z_error == NULL || + zlib.z_initcomp == NULL|| zlib.z_compress == NULL || + zlib.z_finicomp == NULL) { (void) dlclose(zlib.z_dlp); bzero(&zlib, sizeof (zlib)); return (ctf_set_open_errno(errp, ECTF_ZINIT)); @@ -111,6 +145,31 @@ z_strerror(int err) return (zlib.z_error(err)); } +int +z_compress(void *dst, size_t *dstlen, const void *src, size_t srclen) +{ + z_stream zs; + int err; + + bzero(&zs, sizeof (z_stream)); + zs.next_in = (uchar_t *)src; + zs.avail_in = srclen; + zs.next_out = dst; + zs.avail_out = *dstlen; + + if ((err = zlib.z_initcomp(&zs, Z_BEST_COMPRESSION, ZLIB_VERSION, + sizeof (z_stream))) != Z_OK) + return (err); + + if ((err = zlib.z_compress(&zs, Z_FINISH)) != Z_STREAM_END) { + (void) zlib.z_finicomp(&zs); + return (err == Z_OK ? Z_BUF_ERROR : err); + } + + *dstlen = zs.total_out; + return (zlib.z_finicomp(&zs)); +} + /* * Convert a 32-bit ELF file header into GElf. */ @@ -189,7 +248,7 @@ ctf_sect_munmap(const ctf_sect_t *sp) * responsible for closing the file descriptor when it is no longer needed. */ ctf_file_t * -ctf_fdopen(int fd, int *errp) +ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp) { ctf_sect_t ctfsect, symsect, strsect; ctf_file_t *fp = NULL; @@ -221,6 +280,9 @@ ctf_fdopen(int fd, int *errp) */ if (nbytes >= sizeof (ctf_preamble_t) && hdr.ctf.ctp_magic == CTF_MAGIC) { + if (ctfp != NULL) + return (ctf_set_open_errno(errp, EINVAL)); + if (hdr.ctf.ctp_version > CTF_VERSION) return (ctf_set_open_errno(errp, ECTF_CTFVERS)); @@ -370,7 +432,8 @@ ctf_fdopen(int fd, int *errp) continue; /* corrupt sh_name field */ if (shp->sh_type == SHT_PROGBITS && - strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) { + strcmp(strs + shp->sh_name, _CTF_SECTION) == 0 && + ctfp == NULL) { ctfsect.cts_name = strs + shp->sh_name; ctfsect.cts_type = shp->sh_type; ctfsect.cts_flags = shp->sh_flags; @@ -397,18 +460,22 @@ ctf_fdopen(int fd, int *errp) free(sp); /* free section header array */ - if (ctfsect.cts_type == SHT_NULL) { - (void) munmap(strs_map, strs_mapsz); - return (ctf_set_open_errno(errp, ECTF_NOCTFDATA)); - } + if (ctfp == NULL) { + if (ctfsect.cts_type == SHT_NULL && ctfp == NULL) { + (void) munmap(strs_map, strs_mapsz); + return (ctf_set_open_errno(errp, + ECTF_NOCTFDATA)); + } - /* - * Now mmap the CTF data, symtab, and strtab sections and - * call ctf_bufopen() to do the rest of the work. - */ - if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) { - (void) munmap(strs_map, strs_mapsz); - return (ctf_set_open_errno(errp, ECTF_MMAP)); + /* + * Now mmap the CTF data, symtab, and strtab sections + * and call ctf_bufopen() to do the rest of the work. + */ + if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) { + (void) munmap(strs_map, strs_mapsz); + return (ctf_set_open_errno(errp, ECTF_MMAP)); + } + ctfp = &ctfsect; } if (symsect.cts_type != SHT_NULL && @@ -418,12 +485,13 @@ ctf_fdopen(int fd, int *errp) (void) ctf_set_open_errno(errp, ECTF_MMAP); goto bad; /* unmap all and abort */ } - fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp); + fp = ctf_bufopen(ctfp, &symsect, &strsect, errp); } else - fp = ctf_bufopen(&ctfsect, NULL, NULL, errp); + fp = ctf_bufopen(ctfp, NULL, NULL, errp); bad: if (fp == NULL) { - ctf_sect_munmap(&ctfsect); + if (ctfp == NULL) + ctf_sect_munmap(&ctfsect); ctf_sect_munmap(&symsect); ctf_sect_munmap(&strsect); } else @@ -436,6 +504,12 @@ bad: return (ctf_set_open_errno(errp, ECTF_FMT)); } +ctf_file_t * +ctf_fdopen(int fd, int *errp) +{ + return (ctf_fdcreate_int(fd, errp, NULL)); +} + /* * Open the specified file and return a pointer to a CTF container. The file * can be either an ELF file or raw CTF file. This is just a convenient diff --git a/usr/src/lib/libctf/common/ctf_merge.c b/usr/src/lib/libctf/common/ctf_merge.c new file mode 100644 index 0000000000..70676bb0fd --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_merge.c @@ -0,0 +1,1231 @@ +/* + * 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. + */ + +/* + * To perform a merge of two CTF containers, we first diff the two containers + * types. For every type that's in the src container, but not in the dst + * container, we note it and add it to dst container. If there are any objects + * or functions associated with src, we go through and update the types that + * they refer to such that they all refer to types in the dst container. + * + * The bulk of the logic for the merge, after we've run the diff, occurs in + * ctf_merge_common(). + * + * In terms of exported APIs, we don't really export a simple merge two + * containers, as the general way this is used, in something like ctfmerge(1), + * is to add all the containers and then let us figure out the best way to merge + * it. In the future we'll want to grow some control over the number of threads + * that we use to do this, if we end up wanting a muli-threaded merge. If we do, + * we should take care to do the merge in the same way every time. + */ + +#include <ctf_impl.h> +#include <libctf.h> +#include <sys/debug.h> +#include <sys/list.h> +#include <stddef.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdio.h> + +typedef struct ctf_merge_tinfo { + uint16_t cmt_map; /* Map to the type in out */ + boolean_t cmt_fixup; + boolean_t cmt_forward; + boolean_t cmt_missing; +} ctf_merge_tinfo_t; + +/* + * State required for doing an individual merge of two containers. + */ +typedef struct ctf_merge_types { + ctf_file_t *cm_out; /* Output CTF file */ + ctf_file_t *cm_src; /* Input CTF file */ + ctf_merge_tinfo_t *cm_tmap; /* Type state information */ +} ctf_merge_types_t; + +typedef struct ctf_merge_objmap { + list_node_t cmo_node; + const char *cmo_name; /* Symbol name */ + ulong_t cmo_idx; /* Symbol ID */ + ctf_id_t cmo_tid; /* Type ID */ +} ctf_merge_objmap_t; + +typedef struct ctf_merge_funcmap { + list_node_t cmf_node; + const char *cmf_name; /* Symbol name */ + ulong_t cmf_idx; /* Symbol ID */ + ctf_id_t cmf_rtid; /* Type ID */ + uint_t cmf_flags; /* ctf_funcinfo_t ctc_flags */ + uint_t cmf_argc; /* Number of arguments */ + ctf_id_t cmf_args[]; /* Types of arguments */ +} ctf_merge_funcmap_t; + +typedef struct ctf_merge_input { + list_node_t cmi_node; + ctf_file_t *cmi_input; + list_t cmi_omap; + list_t cmi_fmap; +} ctf_merge_input_t; + +struct ctf_merge_handle { + ctf_file_t *cmh_output; /* Final output */ + list_t cmh_inputs; /* Input list */ + ctf_file_t *cmh_unique; /* ctf to uniquify against */ + boolean_t cmh_msyms; /* Should we merge symbols/funcs? */ + int cmh_ofd; /* FD for output file */ + int cmh_flags; /* Flags that control merge behavior */ + char *cmh_label; /* Optional label */ + char *cmh_pname; /* Parent name */ +}; + +static int ctf_merge_add_type(ctf_merge_types_t *, ctf_id_t); + +static void +ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, + ctf_id_t oid, void *arg) +{ + ctf_merge_types_t *cmp = arg; + ctf_merge_tinfo_t *cmt = cmp->cm_tmap; + + if (same == B_TRUE) { + if (ctf_type_kind(ifp, iid) == CTF_K_FORWARD && + ctf_type_kind(ofp, oid) != CTF_K_FORWARD) { + VERIFY(cmt[oid].cmt_map == 0); + cmt[oid].cmt_map = iid; + cmt[oid].cmt_forward = B_TRUE; + return; + } + + /* + * We could have multiple things that a given type ends up + * matching in the world of forwards and pointers to forwards. + * For now just take the first one... + */ + if (cmt[oid].cmt_map != 0) + return; + cmt[oid].cmt_map = iid; + } else if (ifp == cmp->cm_src) { + VERIFY(cmt[iid].cmt_map == 0); + cmt[iid].cmt_missing = B_TRUE; + } +} + +static int +ctf_merge_add_number(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int ret, flags; + const ctf_type_t *tp; + const char *name; + ctf_encoding_t en; + + if (ctf_type_encoding(cmp->cm_src, id, &en) != 0) + return (CTF_ERR); + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + name = ctf_strraw(cmp->cm_src, tp->ctt_name); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + ret = ctf_add_encoded(cmp->cm_out, flags, name, &en, + ctf_type_kind(cmp->cm_src, id)); + + if (ret == CTF_ERR) + return (ret); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = ret; + return (0); +} + +static int +ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int ret, flags; + const ctf_type_t *tp; + ctf_arinfo_t ar; + + if (ctf_array_info(cmp->cm_src, id, &ar) == CTF_ERR) + return (CTF_ERR); + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + if (cmp->cm_tmap[ar.ctr_contents].cmt_map == 0) { + ret = ctf_merge_add_type(cmp, ar.ctr_contents); + if (ret != 0) + return (ret); + ASSERT(cmp->cm_tmap[ar.ctr_contents].cmt_map != 0); + } + ar.ctr_contents = cmp->cm_tmap[ar.ctr_contents].cmt_map; + + if (cmp->cm_tmap[ar.ctr_index].cmt_map == 0) { + ret = ctf_merge_add_type(cmp, ar.ctr_index); + if (ret != 0) + return (ret); + ASSERT(cmp->cm_tmap[ar.ctr_index].cmt_map != 0); + } + ar.ctr_index = cmp->cm_tmap[ar.ctr_index].cmt_map; + + ret = ctf_add_array(cmp->cm_out, flags, &ar); + if (ret == CTF_ERR) + return (ret); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = ret; + + return (0); +} + +static int +ctf_merge_add_reftype(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int ret, flags; + const ctf_type_t *tp; + ctf_id_t reftype; + const char *name; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + name = ctf_strraw(cmp->cm_src, tp->ctt_name); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + reftype = ctf_type_reference(cmp->cm_src, id); + if (reftype == CTF_ERR) + return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src))); + + if (cmp->cm_tmap[reftype].cmt_map == 0) { + ret = ctf_merge_add_type(cmp, reftype); + if (ret != 0) + return (ret); + ASSERT(cmp->cm_tmap[reftype].cmt_map != 0); + } + reftype = cmp->cm_tmap[reftype].cmt_map; + + ret = ctf_add_reftype(cmp->cm_out, flags, name, reftype, + ctf_type_kind(cmp->cm_src, id)); + if (ret == CTF_ERR) + return (ret); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = ret; + return (0); +} + +static int +ctf_merge_add_typedef(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int ret, flags; + const ctf_type_t *tp; + const char *name; + ctf_id_t reftype; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + name = ctf_strraw(cmp->cm_src, tp->ctt_name); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + reftype = ctf_type_reference(cmp->cm_src, id); + if (reftype == CTF_ERR) + return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src))); + + if (cmp->cm_tmap[reftype].cmt_map == 0) { + ret = ctf_merge_add_type(cmp, reftype); + if (ret != 0) + return (ret); + ASSERT(cmp->cm_tmap[reftype].cmt_map != 0); + } + reftype = cmp->cm_tmap[reftype].cmt_map; + + ret = ctf_add_typedef(cmp->cm_out, flags, name, reftype); + if (ret == CTF_ERR) + return (ret); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = ret; + return (0); +} + +typedef struct ctf_merge_enum { + ctf_file_t *cme_fp; + ctf_id_t cme_id; +} ctf_merge_enum_t; + +static int +ctf_merge_add_enumerator(const char *name, int value, void *arg) +{ + ctf_merge_enum_t *cmep = arg; + + return (ctf_add_enumerator(cmep->cme_fp, cmep->cme_id, name, value) == + CTF_ERR); +} + +static int +ctf_merge_add_enum(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int flags; + const ctf_type_t *tp; + const char *name; + ctf_id_t enumid; + ctf_merge_enum_t cme; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + name = ctf_strraw(cmp->cm_src, tp->ctt_name); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + enumid = ctf_add_enum(cmp->cm_out, flags, name); + if (enumid == CTF_ERR) + return (enumid); + + cme.cme_fp = cmp->cm_out; + cme.cme_id = enumid; + if (ctf_enum_iter(cmp->cm_src, id, ctf_merge_add_enumerator, + &cme) != 0) + return (CTF_ERR); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = enumid; + return (0); +} + +static int +ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int ret, flags, i; + const ctf_type_t *tp; + ctf_funcinfo_t ctc; + ctf_id_t *argv; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + if (ctf_func_info_by_id(cmp->cm_src, id, &ctc) == CTF_ERR) + return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src))); + + argv = ctf_alloc(sizeof (ctf_id_t) * ctc.ctc_argc); + if (argv == NULL) + return (ctf_set_errno(cmp->cm_out, ENOMEM)); + if (ctf_func_args_by_id(cmp->cm_src, id, ctc.ctc_argc, argv) == + CTF_ERR) { + ctf_free(argv, sizeof (ctf_id_t) * ctc.ctc_argc); + return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src))); + } + + if (cmp->cm_tmap[ctc.ctc_return].cmt_map == 0) { + ret = ctf_merge_add_type(cmp, ctc.ctc_return); + if (ret != 0) + return (ret); + ASSERT(cmp->cm_tmap[ctc.ctc_return].cmt_map != 0); + } + ctc.ctc_return = cmp->cm_tmap[ctc.ctc_return].cmt_map; + + for (i = 0; i < ctc.ctc_argc; i++) { + if (cmp->cm_tmap[argv[i]].cmt_map == 0) { + ret = ctf_merge_add_type(cmp, argv[i]); + if (ret != 0) + return (ret); + ASSERT(cmp->cm_tmap[argv[i]].cmt_map != 0); + } + argv[i] = cmp->cm_tmap[argv[i]].cmt_map; + } + + ret = ctf_add_funcptr(cmp->cm_out, flags, &ctc, argv); + ctf_free(argv, sizeof (ctf_id_t) * ctc.ctc_argc); + if (ret == CTF_ERR) + return (ret); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = ret; + return (0); +} + +static int +ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int ret, flags; + const ctf_type_t *tp; + const char *name; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + name = ctf_strraw(cmp->cm_src, tp->ctt_name); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + + /* + * ctf_add_forward tries to check to see if a given forward already + * exists in one of its hash tables. If we're here then we know that we + * have a forward in a container that isn't present in another. + * Therefore, we choose a token hash table to satisfy the API choice + * here. + */ + ret = ctf_add_forward(cmp->cm_out, flags, name, CTF_K_STRUCT); + if (ret == CTF_ERR) + return (CTF_ERR); + + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = ret; + return (0); +} + +typedef struct ctf_merge_su { + ctf_merge_types_t *cms_cm; + ctf_id_t cms_id; +} ctf_merge_su_t; + +static int +ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg) +{ + ctf_merge_su_t *cms = arg; + + VERIFY(cms->cms_cm->cm_tmap[type].cmt_map != 0); + type = cms->cms_cm->cm_tmap[type].cmt_map; + + return (ctf_add_member(cms->cms_cm->cm_out, cms->cms_id, name, + type, offset) == CTF_ERR); +} + +/* + * During the first pass, we always add the generic structure and union but none + * of its members as they might not all have been mapped yet. Instead we just + * mark all structures and unions as needing to be fixed up. + */ +static int +ctf_merge_add_su(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward) +{ + int flags, kind; + const ctf_type_t *tp; + const char *name; + ctf_id_t suid; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + name = ctf_strraw(cmp->cm_src, tp->ctt_name); + if (CTF_INFO_ISROOT(tp->ctt_info) != 0) + flags = CTF_ADD_ROOT; + else + flags = CTF_ADD_NONROOT; + kind = ctf_type_kind(cmp->cm_src, id); + + if (kind == CTF_K_STRUCT) + suid = ctf_add_struct(cmp->cm_out, flags, name); + else + suid = ctf_add_union(cmp->cm_out, flags, name); + + if (suid == CTF_ERR) + return (suid); + + /* + * If this is a forward reference then it's mapping should already + * exist. + */ + if (forward == B_FALSE) { + VERIFY(cmp->cm_tmap[id].cmt_map == 0); + cmp->cm_tmap[id].cmt_map = suid; + } else { + VERIFY(cmp->cm_tmap[id].cmt_map == suid); + } + cmp->cm_tmap[id].cmt_fixup = B_TRUE; + + return (0); +} + +static int +ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int kind, ret; + + /* + * We may end up evaluating a type more than once as we may deal with it + * as we recursively evaluate some kind of reference and then we may see + * it normally. + */ + if (cmp->cm_tmap[id].cmt_map != 0) + return (0); + + kind = ctf_type_kind(cmp->cm_src, id); + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + ret = ctf_merge_add_number(cmp, id); + break; + case CTF_K_ARRAY: + ret = ctf_merge_add_array(cmp, id); + break; + case CTF_K_POINTER: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ret = ctf_merge_add_reftype(cmp, id); + break; + case CTF_K_TYPEDEF: + ret = ctf_merge_add_typedef(cmp, id); + break; + case CTF_K_ENUM: + ret = ctf_merge_add_enum(cmp, id); + break; + case CTF_K_FUNCTION: + ret = ctf_merge_add_func(cmp, id); + break; + case CTF_K_FORWARD: + ret = ctf_merge_add_forward(cmp, id); + break; + case CTF_K_STRUCT: + case CTF_K_UNION: + ret = ctf_merge_add_su(cmp, id, B_FALSE); + break; + case CTF_K_UNKNOWN: + /* + * We don't add uknown types, and we later assert that nothing + * should reference them. + */ + return (0); + default: + abort(); + } + + return (ret); +} + +static int +ctf_merge_fixup_su(ctf_merge_types_t *cmp, ctf_id_t id) +{ + ctf_dtdef_t *dtd; + ctf_merge_su_t cms; + ctf_id_t mapid; + + mapid = cmp->cm_tmap[id].cmt_map; + VERIFY(mapid != 0); + dtd = ctf_dtd_lookup(cmp->cm_out, mapid); + VERIFY(dtd != NULL); + + cms.cms_cm = cmp; + cms.cms_id = mapid; + if (ctf_member_iter(cmp->cm_src, id, ctf_merge_add_member, &cms) != 0) + return (CTF_ERR); + + return (0); +} + +static int +ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id) +{ + int kind, ret; + + kind = ctf_type_kind(cmp->cm_src, id); + switch (kind) { + case CTF_K_STRUCT: + case CTF_K_UNION: + ret = ctf_merge_fixup_su(cmp, id); + break; + default: + VERIFY(0); + ret = CTF_ERR; + } + + return (ret); +} + + +/* + * We're going to do three passes over the containers. + * + * Pass 1 checks for forward references in the output container that we know + * exist in the source container. + * + * Pass 2 adds all the missing types from the source container. As part of this + * we may be adding a type as a forward reference that doesn't exist yet. + * Any types that we encounter in this form, we need to add to a third pass. + * + * Pass 3 is the fixup pass. Here we go through and find all the types that were + * missing in the first. + * + * Importantly, we *must* call ctf_update between the second and third pass, + * otherwise several of the libctf functions will not properly find the data in + * the container. + */ +static int +ctf_merge_common(ctf_merge_types_t *cmp) +{ + int ret, i; + + /* Pass 1 */ + for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { + if (cmp->cm_tmap[i].cmt_forward == B_TRUE) { + ret = ctf_merge_add_su(cmp, i, B_TRUE); + if (ret != 0) + return (ret); + } + } + + /* Pass 2 */ + for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { + if (cmp->cm_tmap[i].cmt_missing == B_TRUE) { + ret = ctf_merge_add_type(cmp, i); + if (ret != 0) + return (ret); + } + } + + ret = ctf_update(cmp->cm_out); + if (ret != 0) + return (ret); + + /* Pass 3 */ + for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { + if (cmp->cm_tmap[i].cmt_fixup == B_TRUE) { + ret = ctf_merge_fixup_type(cmp, i); + if (ret != 0) + return (ret); + } + } + + return (0); +} + +/* + * Uniquification is slightly different from a stock merge. For starters, we + * don't need to replace any forward references in the output. In this case + * though, the types that already exist are in a parent container to the empty + * output container. + */ +static int +ctf_merge_uniquify_types(ctf_merge_types_t *cmp) +{ + int i, ret; + + for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { + if (cmp->cm_tmap[i].cmt_missing == B_FALSE) + continue; + ret = ctf_merge_add_type(cmp, i); + if (ret != 0) + return (ret); + } + + ret = ctf_update(cmp->cm_out); + if (ret != 0) + return (ret); + + for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { + if (cmp->cm_tmap[i].cmt_fixup == B_FALSE) + continue; + ret = ctf_merge_fixup_type(cmp, i); + if (ret != 0) + return (ret); + } + + return (0); +} + +static int +ctf_merge_types_init(ctf_merge_types_t *cmp) +{ + cmp->cm_tmap = ctf_alloc(sizeof (ctf_merge_tinfo_t) * + (cmp->cm_src->ctf_typemax + 1)); + if (cmp->cm_tmap == NULL) + return (ctf_set_errno(cmp->cm_out, ENOMEM)); + bzero(cmp->cm_tmap, sizeof (ctf_merge_tinfo_t) * + (cmp->cm_src->ctf_typemax + 1)); + return (0); +} + +static void +ctf_merge_types_fini(ctf_merge_types_t *cmp) +{ + ctf_free(cmp->cm_tmap, sizeof (ctf_merge_tinfo_t) * + (cmp->cm_src->ctf_typemax + 1)); +} + +/* + * Merge types from targ into dest. + */ +static int +ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi) +{ + int ret; + ctf_merge_types_t cm; + ctf_diff_t *cdp; + ctf_merge_objmap_t *cmo; + ctf_merge_funcmap_t *cmf; + ctf_file_t *out = *outp; + ctf_file_t *source = cmi->cmi_input; + + if (!(out->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(out, ECTF_RDONLY)); + + if (ctf_getmodel(out) != ctf_getmodel(source)) + return (ctf_set_errno(out, ECTF_DMODEL)); + + if ((ret = ctf_diff_init(out, source, &cdp)) != 0) + return (ret); + + cm.cm_out = out; + cm.cm_src = source; + ret = ctf_merge_types_init(&cm); + if (ret != 0) { + ctf_diff_fini(cdp); + return (ctf_set_errno(out, ret)); + } + + ret = ctf_diff_types(cdp, ctf_merge_diffcb, &cm); + if (ret != 0) + goto cleanup; + ret = ctf_merge_common(&cm); + if (ret == 0) + ret = ctf_update(out); + else + goto cleanup; + + /* + * Now we need to fix up the object and function maps. + */ + for (cmo = list_head(&cmi->cmi_omap); cmo != NULL; + cmo = list_next(&cmi->cmi_omap, cmo)) { + if (cmo->cmo_tid == 0) + continue; + VERIFY(cm.cm_tmap[cmo->cmo_tid].cmt_map != 0); + cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map; + } + + for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL; + cmf = list_next(&cmi->cmi_fmap, cmf)) { + int i; + + VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0); + cmf->cmf_rtid = cm.cm_tmap[cmf->cmf_rtid].cmt_map; + for (i = 0; i < cmf->cmf_argc; i++) { + VERIFY(cm.cm_tmap[cmf->cmf_args[i]].cmt_map != 0); + cmf->cmf_args[i] = cm.cm_tmap[cmf->cmf_args[i]].cmt_map; + } + } + +cleanup: + ctf_merge_types_fini(&cm); + ctf_diff_fini(cdp); + return (ret); +} + +static int +ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t **outp) +{ + int err, ret; + ctf_file_t *out; + ctf_merge_types_t cm; + ctf_diff_t *cdp; + ctf_merge_input_t *cmi; + ctf_file_t *src = cmh->cmh_output; + ctf_file_t *parent = cmh->cmh_unique; + + *outp = NULL; + if (cmh->cmh_ofd == -1) + out = ctf_create(&err); + else + out = ctf_fdcreate(cmh->cmh_ofd, &err); + if (out == NULL) + return (ctf_set_errno(src, err)); + + out->ctf_parname = cmh->cmh_pname; + if (ctf_setmodel(out, ctf_getmodel(parent)) != 0) { + (void) ctf_set_errno(src, ctf_errno(out)); + ctf_close(out); + return (CTF_ERR); + } + + if (ctf_import(out, parent) != 0) { + (void) ctf_set_errno(src, ctf_errno(out)); + ctf_close(out); + return (CTF_ERR); + } + + if ((ret = ctf_diff_init(parent, src, &cdp)) != 0) { + ctf_close(out); + return (ctf_set_errno(src, ctf_errno(parent))); + } + + cm.cm_out = parent; + cm.cm_src = src; + ret = ctf_merge_types_init(&cm); + if (ret != 0) { + ctf_close(out); + ctf_diff_fini(cdp); + return (ctf_set_errno(src, ret)); + } + + ret = ctf_diff_types(cdp, ctf_merge_diffcb, &cm); + if (ret == 0) { + cm.cm_out = out; + ret = ctf_merge_uniquify_types(&cm); + if (ret == 0) + ret = ctf_update(out); + } + + if (ret != 0) { + ctf_merge_types_fini(&cm); + ctf_diff_fini(cdp); + return (ctf_set_errno(src, ctf_errno(cm.cm_out))); + } + + for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL; + cmi = list_next(&cmh->cmh_inputs, cmi)) { + ctf_merge_objmap_t *cmo; + ctf_merge_funcmap_t *cmf; + + for (cmo = list_head(&cmi->cmi_omap); cmo != NULL; + cmo = list_next(&cmi->cmi_omap, cmo)) { + if (cmo->cmo_tid == 0) + continue; + VERIFY(cm.cm_tmap[cmo->cmo_tid].cmt_map != 0); + cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map; + } + + for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL; + cmf = list_next(&cmi->cmi_fmap, cmf)) { + int i; + + VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0); + cmf->cmf_rtid = cm.cm_tmap[cmf->cmf_rtid].cmt_map; + for (i = 0; i < cmf->cmf_argc; i++) { + VERIFY(cm.cm_tmap[cmf->cmf_args[i]].cmt_map != + 0); + cmf->cmf_args[i] = + cm.cm_tmap[cmf->cmf_args[i]].cmt_map; + } + } + } + + ctf_merge_types_fini(&cm); + ctf_diff_fini(cdp); + *outp = out; + return (0); +} + +static void +ctf_merge_fini_input(ctf_merge_input_t *cmi) +{ + ctf_merge_objmap_t *cmo; + ctf_merge_funcmap_t *cmf; + + while ((cmo = list_remove_head(&cmi->cmi_omap)) != NULL) + ctf_free(cmo, sizeof (ctf_merge_objmap_t)); + + while ((cmf = list_remove_head(&cmi->cmi_fmap)) != NULL) + ctf_free(cmf, sizeof (ctf_merge_funcmap_t) + + sizeof (ctf_id_t) * cmf->cmf_argc); + + ctf_free(cmi, sizeof (ctf_merge_input_t)); +} + +void +ctf_merge_fini(ctf_merge_t *cmh) +{ + size_t len; + ctf_merge_input_t *cmi; + + if (cmh->cmh_output != NULL) + ctf_close(cmh->cmh_output); + + if (cmh->cmh_label != NULL) { + len = strlen(cmh->cmh_label) + 1; + ctf_free(cmh->cmh_label, len); + } + + if (cmh->cmh_pname != NULL) { + len = strlen(cmh->cmh_pname) + 1; + ctf_free(cmh->cmh_pname, len); + } + + while ((cmi = list_remove_head(&cmh->cmh_inputs)) != NULL) + ctf_merge_fini_input(cmi); + + ctf_free(cmh, sizeof (ctf_merge_t)); +} + +ctf_merge_t * +ctf_merge_init(int fd, int *errp) +{ + int err; + ctf_merge_t *out; + struct stat st; + + if (errp == NULL) + errp = &err; + + if (fd != -1 && fstat(fd, &st) != 0) { + *errp = EINVAL; + return (NULL); + } + + out = ctf_alloc(sizeof (ctf_merge_t)); + if (out == NULL) { + *errp = ENOMEM; + return (NULL); + } + + if (fd == -1) { + out->cmh_output = ctf_create(errp); + out->cmh_msyms = B_FALSE; + } else { + out->cmh_output = ctf_fdcreate(fd, errp); + out->cmh_msyms = B_TRUE; + } + + if (out->cmh_output == NULL) { + ctf_free(out, sizeof (ctf_merge_t)); + return (NULL); + } + + list_create(&out->cmh_inputs, sizeof (ctf_merge_input_t), + offsetof(ctf_merge_input_t, cmi_node)); + out->cmh_unique = NULL; + out->cmh_ofd = fd; + out->cmh_label = NULL; + out->cmh_pname = NULL; + + return (out); +} + +int +ctf_merge_label(ctf_merge_t *cmh, const char *label) +{ + char *dup; + + if (cmh->cmh_output == NULL) + return (EINVAL); + + if (label == NULL) + return (EINVAL); + + dup = ctf_strdup(label); + if (dup == NULL) + return (EAGAIN); + + if (cmh->cmh_label != NULL) { + size_t len = strlen(cmh->cmh_label) + 1; + ctf_free(cmh->cmh_label, len); + } + + cmh->cmh_label = dup; + return (0); +} + +static int +ctf_merge_add_funcs(const char *name, ulong_t idx, ctf_funcinfo_t *fip, + void *arg) +{ + ctf_merge_input_t *cmi = arg; + ctf_merge_funcmap_t *fmap; + + fmap = ctf_alloc(sizeof (ctf_merge_funcmap_t) + + sizeof (ctf_id_t) * fip->ctc_argc); + if (fmap == NULL) + return (ENOMEM); + + fmap->cmf_idx = idx; + fmap->cmf_rtid = fip->ctc_return; + fmap->cmf_flags = fip->ctc_flags; + fmap->cmf_argc = fip->ctc_argc; + fmap->cmf_name = name; + + if (ctf_func_args(cmi->cmi_input, idx, fmap->cmf_argc, + fmap->cmf_args) != 0) { + ctf_free(fmap, sizeof (ctf_merge_funcmap_t) + + sizeof (ctf_id_t) * fip->ctc_argc); + return (ctf_errno(cmi->cmi_input)); + } + + list_insert_tail(&cmi->cmi_fmap, fmap); + return (0); +} + +static int +ctf_merge_add_objs(const char *name, ctf_id_t id, ulong_t idx, void *arg) +{ + ctf_merge_input_t *cmi = arg; + ctf_merge_objmap_t *cmo; + + cmo = ctf_alloc(sizeof (ctf_merge_objmap_t)); + if (cmo == NULL) + return (ENOMEM); + + cmo->cmo_name = name; + cmo->cmo_idx = idx; + cmo->cmo_tid = id; + list_insert_tail(&cmi->cmi_omap, cmo); + return (0); +} + +int +ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input) +{ + int ret; + ctf_merge_input_t *cmi; + + if (cmh->cmh_output == NULL) + return (EINVAL); + + if (input->ctf_flags & LCTF_CHILD) + return (ECTF_MCHILD); + + cmi = ctf_alloc(sizeof (ctf_merge_input_t)); + if (cmi == NULL) + return (ENOMEM); + + cmi->cmi_input = input; + list_create(&cmi->cmi_fmap, sizeof (ctf_merge_funcmap_t), + offsetof(ctf_merge_funcmap_t, cmf_node)); + list_create(&cmi->cmi_omap, sizeof (ctf_merge_funcmap_t), + offsetof(ctf_merge_objmap_t, cmo_node)); + + if (cmh->cmh_msyms == B_TRUE) { + if ((ret = ctf_function_iter(input, ctf_merge_add_funcs, + cmi)) != 0) { + ctf_merge_fini_input(cmi); + return (ret); + } + + if ((ret = ctf_object_iter(input, ctf_merge_add_objs, + cmi)) != 0) { + ctf_merge_fini_input(cmi); + return (ret); + } + } + + list_insert_tail(&cmh->cmh_inputs, cmi); + return (0); +} + +int +ctf_merge_uniquify(ctf_merge_t *cmh, ctf_file_t *u, const char *pname) +{ + char *dup; + + if (cmh->cmh_output == NULL) + return (EINVAL); + if (u->ctf_flags & LCTF_CHILD) + return (ECTF_MCHILD); + if (pname == NULL) + return (EINVAL); + dup = ctf_strdup(pname); + if (dup == NULL) + return (EINVAL); + if (cmh->cmh_pname != NULL) { + size_t len = strlen(cmh->cmh_pname) + 1; + ctf_free(cmh->cmh_pname, len); + } + cmh->cmh_pname = dup; + cmh->cmh_unique = u; + return (0); +} + +static int +ctf_merge_symbols(ctf_merge_t *cmh, ctf_file_t *fp) +{ + int err; + ulong_t i; + + uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data; + + for (i = 0; i < fp->ctf_nsyms; i++) { + const char *name; + ctf_merge_input_t *cmi; + ctf_merge_objmap_t *cmo; + + if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; + int type = ELF32_ST_TYPE(symp->st_info); + if (type != STT_OBJECT) + continue; + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + name = (char *)(strbase + symp->st_name); + } else { + const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; + int type = ELF64_ST_TYPE(symp->st_info); + if (type != STT_OBJECT) + continue; + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + name = (char *)(strbase + symp->st_name); + } + + cmo = NULL; + for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL; + cmi = list_next(&cmh->cmh_inputs, cmi)) { + for (cmo = list_head(&cmi->cmi_omap); cmo != NULL; + cmo = list_next(&cmi->cmi_omap, cmo)) { + if (strcmp(cmo->cmo_name, name) == 0) + goto found; + } + } +found: + if (cmo != NULL) { + if (cmo->cmo_tid == 0) + continue; + if ((err = ctf_add_object(fp, i, cmo->cmo_tid)) != 0) + return (err); + } + } + + return (0); +} + +static int +ctf_merge_functions(ctf_merge_t *cmh, ctf_file_t *fp) +{ + int err; + ulong_t i; + ctf_funcinfo_t fi; + + uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data; + + for (i = 0; i < fp->ctf_nsyms; i++) { + const char *name; + ctf_merge_input_t *cmi; + ctf_merge_funcmap_t *cmf; + + if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; + int type = ELF32_ST_TYPE(symp->st_info); + if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC) + continue; + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + name = (char *)(strbase + symp->st_name); + } else { + const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; + int type = ELF64_ST_TYPE(symp->st_info); + if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC) + continue; + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + name = (char *)(strbase + symp->st_name); + } + + cmf = NULL; + for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL; + cmi = list_next(&cmh->cmh_inputs, cmi)) { + for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL; + cmf = list_next(&cmi->cmi_fmap, cmf)) { + if (strcmp(cmf->cmf_name, name) == 0) + goto found; + } + } +found: + if (cmf != NULL) { + fi.ctc_return = cmf->cmf_rtid; + fi.ctc_argc = cmf->cmf_argc; + fi.ctc_flags = cmf->cmf_flags; + if ((err = ctf_add_function(fp, i, &fi, + cmf->cmf_args)) != 0) + return (err); + } + } + + return (0); + +} + +int +ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **out) +{ + int err; + ctf_merge_input_t *cmi; + boolean_t mset = B_FALSE; + ctf_id_t ltype; + + if (cmh->cmh_label != NULL && cmh->cmh_unique != NULL) { + const char *label = ctf_label_topmost(cmh->cmh_unique); + if (label == NULL) + return (ECTF_NOLABEL); + if (strcmp(label, cmh->cmh_label) != 0) + return (ECTF_LCONFLICT); + } + + /* + * We should consider doing a divide and conquer and parallel merge + * here. If we did, we'd want to use some number of threads to perform + * this operation. + */ + for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL; + cmi = list_next(&cmh->cmh_inputs, cmi)) { + if (mset == B_FALSE) { + if (ctf_setmodel(cmh->cmh_output, + ctf_getmodel(cmi->cmi_input)) != 0) { + return (ctf_errno(cmh->cmh_output)); + } + mset = B_TRUE; + } + err = ctf_merge(&cmh->cmh_output, cmi); + if (err != 0) + return (ctf_errno(cmh->cmh_output)); + } + + if (cmh->cmh_unique != NULL) { + err = ctf_uniquify_types(cmh, out); + if (err != 0) + return (ctf_errno(cmh->cmh_output)); + ctf_close(cmh->cmh_output); + } else { + *out = cmh->cmh_output; + } + + ltype = (*out)->ctf_typemax; + if (((*out)->ctf_flags & LCTF_CHILD) && ltype != 0) + ltype += 0x8000; + if (ctf_add_label(*out, cmh->cmh_label, ltype, 0) != 0) { + return (ctf_errno(*out)); + } + + if (cmh->cmh_msyms == B_TRUE) { + err = ctf_merge_symbols(cmh, *out); + if (err != 0) + return (ctf_errno(*out)); + + err = ctf_merge_functions(cmh, *out); + if (err != 0) + return (ctf_errno(*out)); + } + + err = ctf_update(*out); + if (err != 0) + return (ctf_errno(*out)); + + cmh->cmh_output = NULL; + + return (0); +} diff --git a/usr/src/lib/libctf/common/libctf.h b/usr/src/lib/libctf/common/libctf.h index 3fd69318de..13c7e07e99 100644 --- a/usr/src/lib/libctf/common/libctf.h +++ b/usr/src/lib/libctf/common/libctf.h @@ -23,6 +23,9 @@ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2015, Joyent, Inc. + */ /* * This header file defines the interfaces available from the CTF debugger @@ -32,7 +35,7 @@ * the fullness of time after we gain more experience with the interfaces. * * In the meantime, be aware that any program linked with libctf in this - * release of Solaris is almost guaranteed to break in the next release. + * release of illumos is almost guaranteed to break in the next release. * * In short, do not user this header file or libctf for any purpose. */ @@ -40,8 +43,6 @@ #ifndef _LIBCTF_H #define _LIBCTF_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/ctf_api.h> #ifdef __cplusplus @@ -53,6 +54,39 @@ extern "C" { */ extern int _libctf_debug; +typedef enum ctf_diff_flag { + CTF_DIFF_F_IGNORE_INTNAMES = 0x01, + CTF_DIFF_F_MASK = 0x01 +} ctf_diff_flag_t; + +typedef struct ctf_diff ctf_diff_t; +typedef void (*ctf_diff_type_f)(ctf_file_t *, ctf_id_t, boolean_t, ctf_file_t *, + ctf_id_t, void *); +typedef void (*ctf_diff_func_f)(ctf_file_t *, ulong_t, boolean_t, ctf_file_t *, + ulong_t, void *); +typedef void (*ctf_diff_obj_f)(ctf_file_t *, ulong_t, ctf_id_t, boolean_t, + ctf_file_t *, ulong_t, ctf_id_t, void *); + +extern int ctf_diff_init(ctf_file_t *, ctf_file_t *, ctf_diff_t **); +extern uint_t ctf_diff_getflags(ctf_diff_t *); +extern int ctf_diff_setflags(ctf_diff_t *, uint_t); +extern int ctf_diff_types(ctf_diff_t *, ctf_diff_type_f, void *); +extern int ctf_diff_functions(ctf_diff_t *, ctf_diff_func_f, void *); +extern int ctf_diff_objects(ctf_diff_t *, ctf_diff_obj_f, void *); +extern void ctf_diff_fini(ctf_diff_t *); + +typedef struct ctf_merge_handle ctf_merge_t; +extern ctf_merge_t *ctf_merge_init(int, int *); +extern int ctf_merge_add(ctf_merge_t *, ctf_file_t *); +extern int ctf_merge_label(ctf_merge_t *, const char *); +extern int ctf_merge_uniquify(ctf_merge_t *, ctf_file_t *, const char *); +extern int ctf_merge_merge(ctf_merge_t *, ctf_file_t **); +extern void ctf_merge_fini(ctf_merge_t *); + +#define CTF_ELFWRITE_F_COMPRESS 0x1 +extern int ctf_elffdwrite(ctf_file_t *, int, int, int); +extern int ctf_elfwrite(ctf_file_t *, const char *, const char *, int); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libctf/common/mapfile-vers b/usr/src/lib/libctf/common/mapfile-vers index 5573e8db25..95ddfd5d6c 100644 --- a/usr/src/lib/libctf/common/mapfile-vers +++ b/usr/src/lib/libctf/common/mapfile-vers @@ -23,7 +23,7 @@ # # -# Copyright (c) 2013, Joyent, Inc. All rights reserved. +# Copyright (c) 2015, Joyent, Inc. All rights reserved. # # @@ -53,10 +53,13 @@ SYMBOL_VERSION SUNWprivate_1.2 { ctf_add_enumerator; ctf_add_float; ctf_add_forward; + ctf_add_funcptr; ctf_add_function; ctf_add_integer; + ctf_add_label; ctf_add_member; ctf_add_pointer; + ctf_add_object; ctf_add_restrict; ctf_add_struct; ctf_add_type; @@ -64,17 +67,41 @@ SYMBOL_VERSION SUNWprivate_1.2 { ctf_add_union; ctf_add_volatile; ctf_create; + ctf_dataptr; ctf_delete_type; + ctf_diff_init; + ctf_diff_fini; + ctf_diff_functions; + ctf_diff_getflags; + ctf_diff_objects; + ctf_diff_setflags; + ctf_diff_types; ctf_discard; ctf_dup; + ctf_elffdwrite; + ctf_elfwrite; ctf_enum_value; + ctf_flags; + ctf_func_args_by_id; + ctf_func_info_by_id; + ctf_function_iter; ctf_label_info; ctf_label_iter; ctf_label_topmost; ctf_member_info; + ctf_merge_add; + ctf_merge_fini; + ctf_merge_init; + ctf_merge_label; + ctf_merge_merge; + ctf_merge_uniquify; + ctf_object_iter; ctf_parent_file; + ctf_parent_label; ctf_parent_name; ctf_set_array; + ctf_string_iter; + ctf_symbol_name; ctf_type_align; ctf_type_cmp; ctf_type_compat; 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/libdladm/Makefile b/usr/src/lib/libdladm/Makefile index 92de14cc8a..a74b40f58d 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,14 @@ 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 + all := TARGET = all clean := TARGET = clean clobber := TARGET = clobber @@ -62,7 +71,7 @@ all clean clobber install lint: $(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 5bb56d1440..1cb28706a7 100644 --- a/usr/src/lib/libdladm/Makefile.com +++ b/usr/src/lib/libdladm/Makefile.com @@ -20,6 +20,7 @@ # # # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015, Joyent, Inc. # LIBRARY = libdladm.a @@ -27,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 @@ -36,7 +38,7 @@ include ../../Makefile.rootfs LIBS = $(DYNLIB) $(LINTLIB) LDLIBS += -ldevinfo -lc -linetutil -lsocket -lscf -lrcm -lnvpair \ - -lexacct -lnsl -lkstat -lcurses -lpool + -lexacct -lnsl -lkstat -lcurses -lpool -lvarpd SRCDIR = ../common $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) @@ -48,6 +50,9 @@ CERRWARN += -_gcc=-Wno-unused-label CERRWARN += -_gcc=-Wno-uninitialized CPPFLAGS += -I$(SRCDIR) -D_REENTRANT +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + .KEEP_STATE: all: $(LIBS) diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c index cf113e7357..37823ce913 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 (c) 2015, Joyent, Inc. */ #include <unistd.h> @@ -29,6 +30,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> @@ -418,6 +422,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; default: s = "<unknown error>"; break; @@ -654,6 +661,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; @@ -1132,15 +1142,15 @@ dladm_strs2range(char **prop_val, uint_t val_cnt, * 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]; @@ -1168,13 +1178,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]; @@ -1191,6 +1201,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; } @@ -1268,3 +1292,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 c2fceb25ab..2a0491c048 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 2015, Joyent, Inc. */ #ifndef _LIBDLADM_H @@ -71,6 +72,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 @@ -81,6 +86,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 @@ -173,7 +179,8 @@ typedef enum { DLADM_STATUS_NO_IB_HW_RESOURCE, DLADM_STATUS_INVALID_PKEY_TBL_SIZE, DLADM_STATUS_PORT_NOPROTO, - DLADM_STATUS_INVALID_MTU + DLADM_STATUS_INVALID_MTU, + DLADM_STATUS_BAD_ENCAP } dladm_status_t; typedef enum { @@ -221,6 +228,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 @@ -282,12 +295,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 13169285e3..e1a3177808 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 2015, Joyent, Inc. */ #ifndef _LIBDLADM_IMPL_H @@ -168,6 +169,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 8a3c5759ee..303885e929 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); @@ -617,7 +628,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; @@ -627,11 +639,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. */ @@ -645,7 +657,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 4b0753417c..c9c7906934 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 (c) 2011, Joyent Inc. All rights reserved. */ #include <door.h> @@ -528,12 +529,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 +555,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) { diff --git a/usr/src/lib/libdladm/common/libdloverlay.c b/usr/src/lib/libdladm/common/libdloverlay.c new file mode 100644 index 0000000000..3da9005183 --- /dev/null +++ b/usr/src/lib/libdladm/common/libdloverlay.c @@ -0,0 +1,887 @@ +/* + * 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; + datalink_class_t class; + overlay_ioc_nprops_t oin; + overlay_ioc_propinfo_t oipi; + dladm_overlay_propinfo_t dop; + uint64_t varpdid = UINT64_MAX; + + if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, + NULL, 0) != DLADM_STATUS_OK) + return (DLADM_STATUS_BADARG); + + if (class != DATALINK_CLASS_OVERLAY) + 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) + 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 property %d: %s", 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); + + return (dladm_overlay_walk_varpd_prop(handle, linkid, varpdid, func, + arg)); +} + +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 44f8bb2726..189b848f8e 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. */ #include <stdio.h> @@ -399,7 +400,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; @@ -539,27 +540,41 @@ 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_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 c3eea9a7f1..8d43e2d6b9 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -153,11 +153,13 @@ static pd_getf_t get_zone, get_autopush, get_rate_mod, get_rate, 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_rings_range, get_linkmode_prop, + get_promisc_filtered; static pd_setf_t set_zone, set_rate, set_powermode, set_radio, set_public_prop, set_resource, set_stp_prop, - set_bridge_forward, set_bridge_pvid, set_secondary_macs; + set_bridge_forward, set_bridge_pvid, set_secondary_macs, + set_promisc_filtered; static pd_checkf_t check_zone, check_autopush, check_rate, check_hoplimit, check_encaplim, check_uint32, check_maxbw, check_cpus, @@ -365,6 +367,8 @@ static link_attr_t link_attr[] = { { MAC_PROP_IB_LINKMODE, sizeof (uint32_t), "linkmode"}, + { MAC_PROP_VN_PROMISC_FILTERED, sizeof (boolean_t), "promisc-filtered"}, + { MAC_PROP_SECONDARY_ADDRS, sizeof (mac_secondary_addr_t), "secondary-macs"}, @@ -422,6 +426,11 @@ static val_desc_t link_protect_vals[] = { { "dhcp-nospoof", MPT_DHCPNOSPOOF }, }; +static val_desc_t link_promisc_filtered_vals[] = { + { "off", B_FALSE }, + { "on", B_TRUE }, +}; + static val_desc_t dladm_wlan_radio_vals[] = { { "on", DLADM_WLAN_RADIO_ON }, { "off", DLADM_WLAN_RADIO_OFF } @@ -698,6 +707,12 @@ static prop_desc_t prop_table[] = { set_resource, NULL, get_protection, 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, + DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE }, + + { "allowed-ips", { "--", 0 }, NULL, 0, set_resource, NULL, get_allowedips, check_allowedips, PD_CHECK_ALLOC, @@ -1529,6 +1544,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); @@ -4770,3 +4788,50 @@ get_linkmode_prop(dladm_handle_t handle, prop_desc_t *pdp, *val_cnt = 1; return (DLADM_STATUS_OK); } + +/*ARGSUSED*/ +static dladm_status_t +get_promisc_filtered(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) +{ + char *s; + dladm_status_t status; + boolean_t filt; + + status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags, + perm_flags, &filt, sizeof (filt)); + if (status != DLADM_STATUS_OK) + return (status); + + if (filt != 0) + s = link_promisc_filtered_vals[1].vd_name; + else + s = link_promisc_filtered_vals[0].vd_name; + (void) snprintf(prop_val[0], DLADM_STRSIZE, "%s", s); + + *val_cnt = 1; + return (DLADM_STATUS_OK); +} + +/* ARGSUSED */ +static dladm_status_t +set_promisc_filtered(dladm_handle_t handle, prop_desc_t *pdp, + datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags, + datalink_media_t media) +{ + dld_ioc_macprop_t *dip; + dladm_status_t status = DLADM_STATUS_OK; + + dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name, + 0, &status); + + if (dip == NULL) + return (status); + + (void) memcpy(dip->pr_val, &vdp->vd_val, dip->pr_valsize); + status = i_dladm_macprop(handle, dip, B_TRUE); + + free(dip); + return (status); +} diff --git a/usr/src/lib/libdladm/common/llib-ldladm b/usr/src/lib/libdladm/common/llib-ldladm index 8e5eac0614..e5366fb92d 100644 --- a/usr/src/lib/libdladm/common/llib-ldladm +++ b/usr/src/lib/libdladm/common/llib-ldladm @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Joyent, Inc. */ /*LINTLIBRARY*/ @@ -38,3 +39,4 @@ #include <libdlether.h> #include <libdlsim.h> #include <libdlbridge.h> +#include <libdloverlay.h> diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers index b781c93aff..f2847cab55 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; @@ -270,6 +272,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..aa97afee04 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) 2014, 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,7 +1081,13 @@ 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 (*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 @@ -1070,7 +1106,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); @@ -1130,7 +1172,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 +1196,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 +1615,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/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java index 366d83476b..6983e279fa 100644 --- a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,9 +25,6 @@ Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 Revision 1.1 2006/06/20 23:00:12 rpantos <rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent - -ident "%Z%%M% %I% %E% SMI" - */ @@ -38,7 +35,7 @@ package com.apple.dnssd; public interface DNSSDRecordRegistrar extends DNSSDService { - /** Register an independent {@link DNSRecord}.<P> + /** Register an independent {@link DNSRecord}.<P> @param flags Possible values are SHARED or UNIQUE (see flag type definitions for details). <P> @@ -55,25 +52,22 @@ public interface DNSSDRecordRegistrar extends DNSSDService as defined in nameser.h. <P> @param rrclass - The class of the resource record, as defined in nameser.h + The class of the resource record, as defined in nameser.h (usually 1 for the Internet class). <P> - @param rData + @param rdata The new rdata as it is to appear in the DNS record. <P> @param ttl The time to live of the resource record, in seconds. Pass 0 to use a default value. <P> - @param listener - This object will get called when the service is registered. - <P> @return A {@link DNSSDService} that can be used to abort the record registration. @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. @see RuntimePermission */ - public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, int rrclass, byte[] rdata, int ttl) throws DNSSDException; -} +} diff --git a/usr/src/lib/libdtrace/Makefile.com b/usr/src/lib/libdtrace/Makefile.com index f43915ec06..9f3002320a 100644 --- a/usr/src/lib/libdtrace/Makefile.com +++ b/usr/src/lib/libdtrace/Makefile.com @@ -86,6 +86,7 @@ DLIBSRCS += \ io.d \ ip.d \ iscsit.d \ + mac.d \ net.d \ nfs.d \ nfssrv.d \ @@ -98,7 +99,8 @@ DLIBSRCS += \ sysevent.d \ tcp.d \ udp.d \ - unistd.d + unistd.d \ + vnd.d include ../../Makefile.lib @@ -111,6 +113,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 @@ -132,7 +135,7 @@ CERRWARN += -_gcc=-Wno-uninitialized CERRWARN += -_gcc=-Wno-switch 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_decl.c b/usr/src/lib/libdtrace/common/dt_decl.c index bbb561d027..c9bd469ddb 100644 --- a/usr/src/lib/libdtrace/common/dt_decl.c +++ b/usr/src/lib/libdtrace/common/dt_decl.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014 by Delphix. All rights reserved. - * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright (c) 2015 Joyent, Inc. All rights reserved. */ #include <strings.h> @@ -652,7 +652,7 @@ dt_decl_member(dt_node_t *dnp) } if (ctf_add_member(dsp->ds_ctfp, dsp->ds_type, - ident, dtt.dtt_type) == CTF_ERR) { + ident, dtt.dtt_type, ULONG_MAX) == CTF_ERR) { xyerror(D_UNKNOWN, "failed to define member '%s': %s\n", idname, ctf_errmsg(ctf_errno(dsp->ds_ctfp))); } diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h index c6ed8ceaac..2d824ba640 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_open.c b/usr/src/lib/libdtrace/common/dt_open.c index ffb2fe9a8f..74a9cccfef 100644 --- a/usr/src/lib/libdtrace/common/dt_open.c +++ b/usr/src/lib/libdtrace/common/dt_open.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. * Copyright (c) 2012 by Delphix. All rights reserved. */ @@ -40,6 +40,7 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <zone.h> #define _POSIX_PTHREAD_SEMANTICS #include <dirent.h> @@ -683,8 +684,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 */ @@ -824,6 +825,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; @@ -956,11 +959,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; @@ -1134,13 +1145,13 @@ alloc: * Add intrinsic pointer types that are needed to initialize printf * format dictionary types (see table in dt_printf.c). */ - (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, + (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, ctf_lookup_by_name(dmp->dm_ctfp, "void")); - (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, + (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, ctf_lookup_by_name(dmp->dm_ctfp, "char")); - (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, + (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, ctf_lookup_by_name(dmp->dm_ctfp, "int")); if (ctf_update(dmp->dm_ctfp) != 0) { @@ -1200,11 +1211,11 @@ alloc: ctc.ctc_argc = 0; ctc.ctc_flags = 0; - dtp->dt_type_func = ctf_add_function(dmp->dm_ctfp, + dtp->dt_type_func = ctf_add_funcptr(dmp->dm_ctfp, CTF_ADD_ROOT, &ctc, NULL); - dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, - CTF_ADD_ROOT, dtp->dt_type_func); + dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, + dtp->dt_type_func); /* * We also insert CTF definitions for the special D intrinsic types @@ -1305,9 +1316,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 201b50a177..be985d6dab 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) { @@ -988,6 +1028,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 } }; @@ -1068,9 +1109,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_parser.c b/usr/src/lib/libdtrace/common/dt_parser.c index 753009f857..4e7c0c8869 100644 --- a/usr/src/lib/libdtrace/common/dt_parser.c +++ b/usr/src/lib/libdtrace/common/dt_parser.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent Inc. All rights reserved. + * Copyright (c) 2015, Joyent Inc. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. */ @@ -288,7 +288,7 @@ dt_type_pointer(dtrace_typeinfo_t *tip) return (dt_set_errno(dtp, EDT_CTF)); } - ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, type); + ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, type); if (ptr == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); 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 065d99f300..8c65ff8bec 100644 --- a/usr/src/lib/libdtrace/common/dtrace.h +++ b/usr/src/lib/libdtrace/common/dtrace.h @@ -82,6 +82,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/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..8cc60ffc4c --- /dev/null +++ b/usr/src/lib/libidspace/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 (c) 2014 Joyent, Inc. All rights reserved. +# + +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) $(LINTLIB) + +LDLIBS += -lc -lumem + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +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/libipadm/common/libipadm.c b/usr/src/lib/libipadm/common/libipadm.c index 527f735e17..068c8d6535 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 2014, Joyent, Inc. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -285,11 +286,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 */ diff --git a/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com b/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com index 9557d11286..72432160c7 100644 --- a/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com +++ b/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com @@ -37,8 +37,8 @@ KMFINC= -I../../../include -I../../../ber_der/inc BERLIB= -lkmf -lkmfberder BERLIB64= $(BERLIB) -OPENSSLLIBS= $(BERLIB) -lcrypto -lcryptoutil -lc -OPENSSLLIBS64= $(BERLIB64) -lcrypto -lcryptoutil -lc +OPENSSLLIBS= $(BERLIB) -lsunw_crypto -lcryptoutil -lc +OPENSSLLIBS64= $(BERLIB64) -lsunw_crypto -lcryptoutil -lc LINTSSLLIBS = $(BERLIB) -lcrypto -lcryptoutil -lc LINTSSLLIBS64 = $(BERLIB64) -lcrypto -lcryptoutil -lc diff --git a/usr/src/lib/libnisdb/db_mindex3.cc b/usr/src/lib/libnisdb/db_mindex3.cc index 886487f8c5..28182544a4 100644 --- a/usr/src/lib/libnisdb/db_mindex3.cc +++ b/usr/src/lib/libnisdb/db_mindex3.cc @@ -282,7 +282,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, @@ -314,7 +314,7 @@ entriesFromLDAPthread(void *voidarg) { stat = 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 2995f1a9c9..8044148355 100644 --- a/usr/src/lib/libnisdb/db_table.cc +++ b/usr/src/lib/libnisdb/db_table.cc @@ -599,7 +599,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); @@ -967,7 +967,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 2908cc99b0..b27889b147 100644 --- a/usr/src/lib/libnisdb/nis_db.cc +++ b/usr/src/lib/libnisdb/nis_db.cc @@ -527,7 +527,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); @@ -737,7 +737,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); @@ -789,7 +789,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); @@ -989,7 +989,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 c8fa67b625..21d6ca76c6 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 2014 Nexenta Systems, Inc. All rights reserved. +# Copyright 2015 Joyent, Inc. # # @@ -467,6 +468,7 @@ $endif SYMBOL_VERSION SUNWprivate_1.5 { global: + _nsl_brand_set_hooks; clnt_create_service_timed; inet_matchaddr; } SUNWprivate_1.4; diff --git a/usr/src/lib/libnsl/netselect/netselect.c b/usr/src/lib/libnsl/netselect/netselect.c index 41dfa4909a..fe8611fd13 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> @@ -70,6 +69,9 @@ static struct netconfig *netconfig_dup(struct netconfig *); extern const char __nsl_dom[]; +static int (*brand_get_sz)(void) = NULL; +static struct netconfig *(*brand_get_net_ent)(int) = NULL; + /* * Static global variables used by the library procedures: * @@ -255,6 +257,14 @@ freenetconfigent(struct netconfig *netp) netconfig_free(netp); } +void +_nsl_brand_set_hooks(int (*set_sz_func)(void), + struct netconfig *(*get_ent_func)(int)) +{ + brand_get_sz = set_sz_func; + brand_get_net_ent = get_ent_func; +} + /* * getnetlist() reads the netconfig file and creates a * NULL-terminated list of entries. @@ -265,52 +275,73 @@ 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 */ - if ((fp = fopen(NETCONFIG, "rF")) == NULL) { - nc_error = NC_OPENFAIL; - return (NULL); - } + if (brand_get_sz != NULL) { + count = brand_get_sz(); + } else { + char line[BUFSIZ]; /* holds each line of NETCONFIG */ - count = 0; - while (fgets(line, BUFSIZ, fp)) { - if (!(blank(line) || comment(line))) { - ++count; + if ((fp = fopen(NETCONFIG, "rF")) == NULL) { + nc_error = NC_OPENFAIL; + return (NULL); + } + + count = 0; + while (fgets(line, BUFSIZ, fp)) { + if (!(blank(line) || comment(line))) { + ++count; + } } + rewind(fp); } - rewind(fp); 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); } - /* - * The following loop fills in the list (loops until - * fgetnetconfig() returns a NULL) and counts the - * number of entries placed in the list. Note that - * when the loop is completed, the last entry in the - * list will contain a NULL (signifying the end of - * the list). - */ - linenum = 0; - for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++) - ; - (void) fclose(fp); + if (brand_get_net_ent != NULL) { + int i; + + tpp = listpp; + for (i = 0; i < count; i++) { + *tpp = brand_get_net_ent(i); + tpp++; + } + *tpp = NULL; + nc_error = NC_NOMOREENTRIES; + } else { + /* + * The following loop fills in the list (loops until + * fgetnetconfig() returns a NULL) and counts the + * number of entries placed in the list. Note that + * when the loop is completed, the last entry in the + * list will contain a NULL (signifying the end of + * the list). + */ + linenum = 0; + for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++) + ; + (void) fclose(fp); + + if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */ + netlist_free(&listpp); + } - 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 5a317f5f94..e59510726f 100644 --- a/usr/src/lib/libnvpair/nvpair_json.c +++ b/usr/src/lib/libnvpair/nvpair_json.c @@ -17,16 +17,72 @@ #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, asize; + 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) { + va_end(ap); + return (-1); + } + if (ret >= size) { + asize = *blen; + while (ret >= 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. @@ -43,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; @@ -51,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) || @@ -83,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; } @@ -104,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; } @@ -203,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; } @@ -225,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; } @@ -240,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; } @@ -255,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; } @@ -270,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; } @@ -284,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; } @@ -298,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; } @@ -312,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; } @@ -326,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; } @@ -340,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; } @@ -354,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; } @@ -368,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; } @@ -383,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; } @@ -398,6 +466,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/libpkg/Makefile.com b/usr/src/lib/libpkg/Makefile.com index 3e3b294c7e..c2a80e0a34 100644 --- a/usr/src/lib/libpkg/Makefile.com +++ b/usr/src/lib/libpkg/Makefile.com @@ -66,7 +66,7 @@ $(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) LIBS = $(DYNLIB) $(LINTLIB) -LDLIBS += -lc -lssl -lwanboot -lcrypto -lscf -ladm +LDLIBS += -lc -lsunw_ssl -lwanboot -lsunw_crypto -lscf -ladm CFLAGS += $(CCVERBOSE) CERRWARN += -_gcc=-Wno-unused-label diff --git a/usr/src/lib/libproc/common/Pcontrol.c b/usr/src/lib/libproc/common/Pcontrol.c index 751c0c3f8a..afa04c43c7 100644 --- a/usr/src/lib/libproc/common/Pcontrol.c +++ b/usr/src/lib/libproc/common/Pcontrol.c @@ -26,6 +26,7 @@ * Portions Copyright 2007 Chad Mynhier * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright 2015, Joyent, Inc. */ #include <assert.h> @@ -337,10 +338,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); @@ -1752,6 +1759,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; @@ -1931,6 +1941,7 @@ Pstopstatus(struct ps_prochandle *P, case PR_FAULTED: case PR_JOBCONTROL: case PR_SUSPENDED: + case PR_BRAND: break; default: errno = EPROTO; @@ -3505,6 +3516,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 6697d5736b..9e3aa1ac7d 100644 --- a/usr/src/lib/libproc/common/Pcontrol.h +++ b/usr/src/lib/libproc/common/Pcontrol.h @@ -24,7 +24,7 @@ */ /* * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. - * Copyright (c) 2014, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. */ @@ -96,6 +96,7 @@ typedef struct file_info { /* symbol information for a mapped file */ struct map_info *file_map; /* primary (text) mapping */ int file_ref; /* references from map_info_t structures */ int file_fd; /* file descriptor for the mapped file */ + int file_dbgfile; /* file descriptor for the debug file */ int file_init; /* 0: initialization yet to be performed */ GElf_Half file_etype; /* ELF e_type from ehdr */ GElf_Half file_class; /* ELF e_ident[EI_CLASS] from ehdr */ @@ -105,6 +106,7 @@ typedef struct file_info { /* symbol information for a mapped file */ char *file_rname; /* resolved on-disk object pathname */ char *file_rbase; /* pointer to basename of file_rname */ Elf *file_elf; /* ELF handle so we can close */ + Elf *file_dbgelf; /* Debug ELF handle so we can close */ void *file_elfmem; /* data for faked-up ELF handle */ sym_tbl_t file_symtab; /* symbol table */ sym_tbl_t file_dynsym; /* dynamic symbol table */ diff --git a/usr/src/lib/libproc/common/Pcore.c b/usr/src/lib/libproc/common/Pcore.c index c899ee1b20..454360a8b4 100644 --- a/usr/src/lib/libproc/common/Pcore.c +++ b/usr/src/lib/libproc/common/Pcore.c @@ -2695,6 +2695,7 @@ Pfgrab_core(int core_fd, const char *aout_path, int *perr) fp->file_ref = 1; fp->file_fd = -1; + fp->file_dbgfile = -1; fp->file_lo = malloc(sizeof (rd_loadobj_t)); fp->file_lname = strdup(execname); diff --git a/usr/src/lib/libproc/common/Pidle.c b/usr/src/lib/libproc/common/Pidle.c index 3191f4fa7e..c69bcaf860 100644 --- a/usr/src/lib/libproc/common/Pidle.c +++ b/usr/src/lib/libproc/common/Pidle.c @@ -226,6 +226,7 @@ Pgrab_file(const char *fname, int *perr) } fp->file_fd = fd; + fp->file_dbgfile = -1; fp->file_lo->rl_lmident = LM_ID_BASE; if ((fp->file_lname = strdup(fp->file_pname)) == NULL) { *perr = G_STRANGE; diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c index 62354f9a7b..a3e0d07ae2 100644 --- a/usr/src/lib/libproc/common/Psymtab.c +++ b/usr/src/lib/libproc/common/Psymtab.c @@ -21,7 +21,7 @@ /* * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. */ @@ -43,6 +43,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/sysmacros.h> +#include <sys/crc32.h> #include "libproc.h" #include "Pcontrol.h" @@ -61,6 +62,7 @@ static int read_ehdr32(struct ps_prochandle *, Elf32_Ehdr *, uint_t *, static int read_ehdr64(struct ps_prochandle *, Elf64_Ehdr *, uint_t *, uintptr_t); #endif +static uint32_t psym_crc32[] = { CRC32_TABLE }; #define DATA_TYPES \ ((1 << STT_OBJECT) | (1 << STT_FUNC) | \ @@ -184,6 +186,7 @@ file_info_new(struct ps_prochandle *P, map_info_t *mptr) mptr->map_file = fptr; fptr->file_ref = 1; fptr->file_fd = -1; + fptr->file_dbgfile = -1; P->num_files++; /* @@ -274,6 +277,10 @@ file_info_free(struct ps_prochandle *P, file_info_t *fptr) free(fptr->file_elfmem); if (fptr->file_fd >= 0) (void) close(fptr->file_fd); + if (fptr->file_dbgelf) + (void) elf_end(fptr->file_dbgelf); + if (fptr->file_dbgfile >= 0) + (void) close(fptr->file_dbgfile); if (fptr->file_ctfp) { ctf_close(fptr->file_ctfp); free(fptr->file_ctf_buf); @@ -1567,6 +1574,170 @@ build_fake_elf(struct ps_prochandle *P, file_info_t *fptr, GElf_Ehdr *ehdr, } /* + * Try and find the file described by path in the file system and validate that + * it matches our CRC before we try and process it for symbol information. + * + * Before we valiate if it's a crc, we check to ensure that it's a normal file + * and not anything else. + */ +static boolean_t +build_alt_debug(file_info_t *fptr, const char *path, uint32_t crc) +{ + int fd; + struct stat st; + Elf *elf; + Elf_Scn *scn; + GElf_Shdr symshdr, strshdr; + Elf_Data *symdata, *strdata; + uint32_t c = -1U; + + if ((fd = open(path, O_RDONLY)) < 0) + return (B_FALSE); + + if (fstat(fd, &st) != 0) { + (void) close(fd); + return (B_FALSE); + } + + if (S_ISREG(st.st_mode) == 0) { + (void) close(fd); + return (B_FALSE); + } + + for (;;) { + char buf[4096]; + ssize_t ret = read(fd, buf, sizeof (buf)); + if (ret == -1) { + if (ret == EINTR) + continue; + (void) close(fd); + return (B_FALSE); + } + if (ret == 0) { + c = ~c; + if (c != crc) { + dprintf("crc mismatch, found: 0x%x " + "expected 0x%x\n", c, crc); + (void) close(fd); + return (B_FALSE); + } + break; + } + CRC32(c, buf, ret, c, psym_crc32); + } + + elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + (void) close(fd); + return (B_FALSE); + } + + if (elf_kind(elf) != ELF_K_ELF) { + goto fail; + } + + /* + * Do two passes, first see if we have a symbol header, then see if we + * can find the corresponding linked string table. + */ + scn = NULL; + for (scn = elf_nextscn(elf, scn); scn != NULL; + scn = elf_nextscn(elf, scn)) { + + if (gelf_getshdr(scn, &symshdr) == NULL) + goto fail; + + if (symshdr.sh_type != SHT_SYMTAB) + continue; + + if ((symdata = elf_getdata(scn, NULL)) == NULL) + goto fail; + + break; + } + if (scn == NULL) + goto fail; + + if ((scn = elf_getscn(elf, symshdr.sh_link)) == NULL) + goto fail; + + if (gelf_getshdr(scn, &strshdr) == NULL) + goto fail; + + if ((strdata = elf_getdata(scn, NULL)) == NULL) + goto fail; + + fptr->file_symtab.sym_data_pri = symdata; + fptr->file_symtab.sym_symn += symshdr.sh_size / symshdr.sh_entsize; + fptr->file_symtab.sym_strs = strdata->d_buf; + fptr->file_symtab.sym_strsz = strdata->d_size; + fptr->file_symtab.sym_hdr_pri = symshdr; + fptr->file_symtab.sym_strhdr = strshdr; + + dprintf("successfully loaded additional debug symbols for %s from %s\n", + fptr->file_rname, path); + + fptr->file_dbgfile = fd; + fptr->file_dbgelf = elf; + return (B_TRUE); +fail: + (void) elf_end(elf); + (void) close(fd); + return (B_FALSE); +} + +/* + * We're here because the object in question has no symbol information, that's a + * bit unfortunate. However, we've found that there's a .gnu_debuglink sitting + * around. By convention that means that given the current location of the + * object on disk, and the debug name that we found in the binary we need to + * search the following locations for a matching file. + * + * <dirname>/.debug/<debug-name> + * /usr/lib/debug/<dirname>/<debug-name> + * + * In the future, we should consider supporting looking in the prefix's + * lib/debug directory for a matching object. + */ +static void +find_alt_debug(file_info_t *fptr, const char *name, uint32_t crc) +{ + boolean_t r; + char *dup = NULL, *path = NULL, *dname; + + dprintf("find_alt_debug: looking for %s, crc 0x%x\n", name, crc); + if (fptr->file_rname == NULL) { + dprintf("find_alt_debug: encountered null file_rname\n"); + return; + } + + dup = strdup(fptr->file_rname); + if (dup == NULL) + return; + + dname = dirname(dup); + if (asprintf(&path, "%s/.debug/%s", dname, name) != -1) { + dprintf("attempting to load alternate debug information " + "from %s\n", path); + r = build_alt_debug(fptr, path, crc); + free(path); + if (r == B_TRUE) + goto out; + } + + if (asprintf(&path, "/usr/lib/debug/%s/%s", dname, name) != -1) { + dprintf("attempting to load alternate debug information " + "from %s\n", path); + r = build_alt_debug(fptr, path, crc); + free(path); + if (r == B_TRUE) + goto out; + } +out: + free(dup); +} + +/* * Build the symbol table for the given mapped file. */ void @@ -1587,7 +1758,8 @@ Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr) GElf_Shdr c_shdr; Elf_Data *c_data; const char *c_name; - } *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL; + } *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL, + *dbglink = NULL; if (fptr->file_init) return; /* We've already processed this file */ @@ -1813,7 +1985,69 @@ Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr) continue; } ctf = cp; + } else if (strcmp(cp->c_name, ".gnu_debuglink") == 0) { + dprintf("found .gnu_debuglink section for %s\n", + fptr->file_rname); + /* + * Let's make sure of a few things before we do this. + */ + if (cp->c_shdr.sh_type == SHT_PROGBITS && + cp->c_data->d_buf != NULL) { + dprintf(".gnu_debuglink pases initial " + "sanity\n"); + dbglink = cp; + } + } + } + + /* + * If we haven't found any symbol table information and we have found a + * .gnu_debuglink, it's time to try and figure out where we might find + * this. To do so, we're going to first verify that the elf data seems + * somewhat sane, eg. the elf data should be a string, so we want to + * verify we have a null-terminator. + */ + if (fptr->file_symtab.sym_data_pri == NULL && dbglink != NULL) { + char *c = dbglink->c_data->d_buf; + size_t i; + boolean_t found = B_FALSE; + Elf_Data *ed = dbglink->c_data; + uint32_t crc; + + for (i = 0; i < ed->d_size; i++) { + if (c[i] == '\0') { + uintptr_t off; + dprintf("got .gnu_debuglink terminator at " + "offset %lu\n", (unsigned long)i); + /* + * After the null terminator, there should be + * padding, followed by a 4 byte CRC of the + * file. If we don't see this, we're going to + * assume this is bogus. + */ + if ((i % sizeof (uint32_t)) == 0) { + i += 4; + } else { + i += sizeof (uint32_t) - + (i % sizeof (uint32_t)); + } + if (i + sizeof (uint32_t) == + dbglink->c_data->d_size) { + found = B_TRUE; + off = (uintptr_t)ed->d_buf + i; + crc = *(uint32_t *)off; + } else { + dprintf(".gnu_debuglink size mismatch, " + "expected: %lu, found: %lu\n", + (unsigned long)i, + (unsigned long)ed->d_size); + } + break; + } } + + if (found == B_TRUE) + find_alt_debug(fptr, dbglink->c_data->d_buf, crc); } /* @@ -1943,7 +2177,13 @@ bad: fptr->file_elfmem = NULL; } (void) close(fptr->file_fd); + if (fptr->file_dbgelf != NULL) + (void) elf_end(fptr->file_dbgelf); + fptr->file_dbgelf = NULL; + if (fptr->file_dbgfile >= 0) + (void) close(fptr->file_dbgfile); fptr->file_fd = -1; + fptr->file_dbgfile = -1; } /* diff --git a/usr/src/lib/libproc/common/Psymtab_machelf32.c b/usr/src/lib/libproc/common/Psymtab_machelf32.c index 13ee1ac8de..1e85b39179 100644 --- a/usr/src/lib/libproc/common/Psymtab_machelf32.c +++ b/usr/src/lib/libproc/common/Psymtab_machelf32.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + #include <assert.h> #include <stdio.h> #include <stdlib.h> @@ -635,7 +639,7 @@ done_with_plt: if (Pread(P, &ndx, sizeof (ndx), (uintptr_t)hash) != sizeof (ndx)) { dprintf("Pread of .hash at %lx failed\n", (long)hash); - goto bad; + goto badplt; } while (ndx) { @@ -643,7 +647,7 @@ done_with_plt: (uintptr_t)&symtabptr[ndx]) != sizeof (sym)) { dprintf("Pread of .symtab at %lx failed\n", (long)&symtabptr[ndx]); - goto bad; + goto badplt; } strtabname = strtabptr + sym.st_name; @@ -651,7 +655,7 @@ done_with_plt: strtabname) < 0) { dprintf("Pread of .strtab at %lx failed\n", (long)strtabname); - goto bad; + goto badplt; } if (strcmp("_PROCEDURE_LINKAGE_TABLE_", strbuf) == 0) @@ -662,7 +666,7 @@ done_with_plt: sizeof (ndx)) { dprintf("Pread of .hash at %lx failed\n", (long)hash); - goto bad; + goto badplt; } } @@ -678,7 +682,7 @@ done_with_plt: if (ndx == 0) { dprintf( "Failed to find \"_PROCEDURE_LINKAGE_TABLE_\"\n"); - goto bad; + goto badplt; } sp->sh_name = SHSTR_NDX_plt; @@ -698,12 +702,13 @@ done_with_plt: sp->sh_size) { dprintf("failed to read .plt at %lx\n", (long)sp->sh_addr); - goto bad; + goto badplt; } off += roundup(sp->sh_size, SH_ADDRALIGN); sp++; } +badplt: /* make sure we didn't write past the end of allocated memory */ sp++; assert(((uintptr_t)(sp) - 1) < ((uintptr_t)elfdata + size)); diff --git a/usr/src/lib/libproc/common/Pzone.c b/usr/src/lib/libproc/common/Pzone.c index 3c4999bbe4..c8e2bfcd02 100644 --- a/usr/src/lib/libproc/common/Pzone.c +++ b/usr/src/lib/libproc/common/Pzone.c @@ -28,6 +28,9 @@ * Copyright (c) 2013 by Delphix. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include <assert.h> #include <dlfcn.h> #include <errno.h> diff --git a/usr/src/lib/librename/Makefile b/usr/src/lib/librename/Makefile new file mode 100644 index 0000000000..222523d9a1 --- /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) $(VARPD_MAPFILES) 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..bbabcbcede --- /dev/null +++ b/usr/src/lib/libresolv2_joy/Makefile.com @@ -0,0 +1,162 @@ +# +# 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. +# +# + +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 + +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..5ac4ebac9b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c @@ -0,0 +1,64 @@ +#ifndef LINT +static const char rcsid[] = "$Id: ftruncate.c,v 1.3 2005/04/27 18:16:45 sra Exp $"; +#endif + +/*! \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..2926a3575e --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c @@ -0,0 +1,62 @@ +#ifndef LINT +static const char rcsid[] = "$Id: gettimeofday.c,v 1.4 2005/04/27 04:56:11 sra Exp $"; +#endif + +#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..001b24b58f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c @@ -0,0 +1,156 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: mktemp.c,v 1.2 2005/04/27 04:56:11 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * 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..2dcbc57e6c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/putenv.c @@ -0,0 +1,27 @@ +#ifndef LINT +static const char rcsid[] = "$Id: putenv.c,v 1.2 2005/04/27 04:56:11 sra Exp $"; +#endif + +#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..5fa691a92f --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/readv.c @@ -0,0 +1,39 @@ +#ifndef LINT +static const char rcsid[] = "$Id: readv.c,v 1.2 2005/04/27 04:56:11 sra Exp $"; +#endif + +#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..baf00f6ff2 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/setenv.c @@ -0,0 +1,151 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)setenv.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: setenv.c,v 1.2 2005/04/27 04:56:11 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * 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..67881d7ca8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c @@ -0,0 +1,29 @@ +#ifndef LINT +static const char rcsid[] = "$Id: setitimer.c,v 1.2 2005/04/27 04:56:12 sra Exp $"; +#endif + +#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..0c9f0dccf0 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c @@ -0,0 +1,124 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: strcasecmp.c,v 1.2 2005/04/27 04:56:12 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * 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..5973e63d5b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strerror.c @@ -0,0 +1,94 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: strerror.c,v 1.6 2008/02/18 03:49:08 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * 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..4c12d88e1c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c @@ -0,0 +1,70 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)strpbrk.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: strpbrk.c,v 1.2 2005/04/27 04:56:12 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* + * 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..c7969f0028 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strsep.c @@ -0,0 +1,88 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: strsep.c,v 1.2 2005/04/27 04:56:12 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 <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..b37ff72729 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c @@ -0,0 +1,119 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: strtoul.c,v 1.4 2008/02/18 03:49:08 marka 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 <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..65baa71cfc --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/bsd/writev.c @@ -0,0 +1,89 @@ +#ifndef LINT +static const char rcsid[] = "$Id: writev.c,v 1.3 2005/04/27 04:56:13 sra Exp $"; +#endif + +#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..a3e48077ad --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/dst_api.c @@ -0,0 +1,1048 @@ +#ifndef LINT +static const char rcsid[] = "$Header: /proj/cvs/prod/libbind/dst/dst_api.c,v 1.17 2007/09/24 17:18:25 each Exp $"; +#endif + +/* + * 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..23925e4269 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c @@ -0,0 +1,491 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifdef HMAC_MD5 +#ifndef LINT +static const char rcsid[] = "$Header: /proj/cvs/prod/libbind/dst/hmac_link.c,v 1.8 2007/09/24 17:18:25 each Exp $"; +#endif +/* + * 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..8f827667d6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/dst/support.c @@ -0,0 +1,342 @@ +static const char rcsid[] = "$Header: /proj/cvs/prod/libbind/dst/support.c,v 1.6 2005/10/11 00:10:13 marka Exp $"; + + +/* + * 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..7c50320ae7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns.c @@ -0,0 +1,154 @@ +/* + * 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: dns.c,v 1.5 2006/03/09 23:57:56 marka Exp $"; +#endif + +/*! \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..67812717fb --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c @@ -0,0 +1,1143 @@ +/* + * 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 $ */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: dns_ho.c,v 1.23 2008/11/14 02:36:51 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* 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..e9acfd39c7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c @@ -0,0 +1,591 @@ +/* + * 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: dns_nw.c,v 1.12 2005/04/27 04:56:22 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* 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..c281be432c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c @@ -0,0 +1,268 @@ +/* + * 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: dns_pr.c,v 1.5 2005/04/27 04:56:22 sra Exp $"; +#endif + +/* 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..1001dc1051 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c @@ -0,0 +1,300 @@ +/* + * 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: dns_sv.c,v 1.5 2005/04/27 04:56:23 sra Exp $"; +#endif + +/* 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..7da01f5b09 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen.c @@ -0,0 +1,459 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: gen.c,v 1.7 2005/04/27 04:56:23 sra Exp $"; +#endif + +/*! \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..8e41d7d610 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c @@ -0,0 +1,391 @@ +/* + * 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: gen_ho.c,v 1.5 2006/03/09 23:57:56 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* 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..e3c874ee68 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c @@ -0,0 +1,174 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: gen_ng.c,v 1.3 2005/04/27 04:56:23 sra Exp $"; +#endif + +/* 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..12bd0e0f6d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_nw.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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: gen_nw.c,v 1.4 2005/04/27 04:56:23 sra Exp $"; +#endif + +/* 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..9fd32c4dd9 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c @@ -0,0 +1,228 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: gen_pr.c,v 1.3 2005/04/27 04:56:24 sra Exp $"; +#endif + +/* 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..93b70e57ec --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c @@ -0,0 +1,229 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: gen_sv.c,v 1.3 2005/04/27 04:56:24 sra Exp $"; +#endif + +/* 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..c4751c2c80 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gethostent.c @@ -0,0 +1,1096 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: gethostent.c,v 1.8 2006/01/10 05:06:00 marka Exp $"; +#endif + +/* 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..1971677c5d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c @@ -0,0 +1,278 @@ +/* + * 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: gethostent_r.c,v 1.9 2005/09/03 12:41:37 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..0d00699e81 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetent.c @@ -0,0 +1,345 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: getnetent.c,v 1.7 2005/04/27 04:56:25 sra Exp $"; +#endif + +/* 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..9fb52bc394 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c @@ -0,0 +1,234 @@ +/* + * 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: getnetent_r.c,v 1.6 2005/09/03 12:41:38 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..40b3e5a8ad --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c @@ -0,0 +1,160 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: getnetgrent.c,v 1.6 2008/11/14 02:36:51 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* 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..aa8810320d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c @@ -0,0 +1,219 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: getnetgrent_r.c,v 1.14 2008/11/14 02:36:51 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..32a1040916 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c @@ -0,0 +1,176 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: getprotoent.c,v 1.4 2005/04/27 04:56:26 sra Exp $"; +#endif + +/* 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..d5d9ae53b6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c @@ -0,0 +1,223 @@ +/* + * 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: getprotoent_r.c,v 1.6 2006/08/01 01:14:16 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..31af67e659 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getservent.c @@ -0,0 +1,179 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: getservent.c,v 1.4 2005/04/27 04:56:26 sra Exp $"; +#endif + +/* 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..42d1e46163 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c @@ -0,0 +1,242 @@ +/* + * 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: getservent_r.c,v 1.6 2006/08/01 01:14:16 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..7641bf80ac --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/hesiod.c @@ -0,0 +1,505 @@ +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: hesiod.c,v 1.7 2005/07/28 06:51:48 marka Exp $"; +#endif + +/* + * 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..ef10631c22 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp.c @@ -0,0 +1,583 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: irp.c,v 1.12 2008/11/14 02:36:51 marka Exp $"; +#endif + +/* Imports */ + +#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..b0de31dc89 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c @@ -0,0 +1,405 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: irp_ho.c,v 1.3 2005/04/27 04:56:28 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* 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..1af862cab4 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c @@ -0,0 +1,253 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: irp_ng.c,v 1.4 2006/12/07 04:46:27 marka Exp $"; +#endif + +/* 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..3f2381f95d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c @@ -0,0 +1,348 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: irp_nw.c,v 1.4 2006/03/09 23:57:56 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#if 0 + +#endif + +/* 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..ea876e8281 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c @@ -0,0 +1,327 @@ +/* + * 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 defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: irp_pr.c,v 1.3 2005/04/27 04:56:29 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* extern */ + +#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..577e697fe6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c @@ -0,0 +1,346 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: irp_sv.c,v 1.3 2005/04/27 04:56:29 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* extern */ + +#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..85ffff1866 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c @@ -0,0 +1,2301 @@ +/* + * 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 defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: irpmarshall.c,v 1.7 2006/03/09 23:57:56 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..f12ca20f38 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/irs_data.c @@ -0,0 +1,254 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: irs_data.c,v 1.12 2007/08/27 03:32:26 marka Exp $"; +#endif + +#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..9dd6967b87 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl.c @@ -0,0 +1,142 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: lcl.c,v 1.4 2005/04/27 04:56:30 sra Exp $"; +#endif + +/* Imports */ + +#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..17c9a5e725 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c @@ -0,0 +1,578 @@ +/* + * 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 $ */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: lcl_ho.c,v 1.5 2006/03/09 23:57:56 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* 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..319725ce70 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c @@ -0,0 +1,446 @@ +/* + * 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(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: lcl_ng.c,v 1.3 2005/04/27 04:56:31 sra Exp $"; +#endif + +/* 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..2e5cd55a6c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c @@ -0,0 +1,373 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: lcl_nw.c,v 1.4 2005/04/27 04:56:31 sra Exp $"; +/* 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 $ */ +#endif /* LIBC_SCCS and not lint */ + +/* 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..e1538530eb --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c @@ -0,0 +1,294 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: lcl_pr.c,v 1.4 2006/03/09 23:57:56 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* extern */ + +#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..ad6526430c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c @@ -0,0 +1,432 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: lcl_sv.c,v 1.4 2005/04/27 04:56:31 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/* extern */ + +#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..ac1796543c --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/nis.c @@ -0,0 +1,156 @@ +/* + * 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: nis.c,v 1.3 2005/04/27 04:56:32 sra Exp $"; +#endif + +/* Imports */ + +#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..504813bde1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c @@ -0,0 +1,127 @@ +/* + * 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: nul_ng.c,v 1.3 2005/04/27 04:56:34 sra Exp $"; +#endif + +/*! \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..9c70eabddc --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/irs/util.c @@ -0,0 +1,109 @@ +/* + * 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: util.c,v 1.3 2005/04/27 04:56:34 sra Exp $"; +#endif + +#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..b71e5a32d3 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/assertions.c @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: assertions.c,v 1.5 2008/11/14 02:36:51 marka Exp $"; +#endif + +#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..79c35722b1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/base64.c @@ -0,0 +1,333 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: base64.c,v 1.4 2005/04/27 04:56:34 sra Exp $"; +#endif /* 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 <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..efe5009292 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: bitncmp.c,v 1.5 2008/11/14 02:36:51 marka Exp $"; +#endif + +#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..f71001a6d4 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c @@ -0,0 +1,620 @@ +/* + * 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. + */ + +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: ctl_clnt.c,v 1.11 2008/11/14 02:36:51 marka Exp $"; +#endif /* not lint */ + +/* Extern. */ + +#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..7ab719a5e6 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c @@ -0,0 +1,188 @@ +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: ctl_p.c,v 1.4 2005/04/27 04:56:35 sra Exp $"; +#endif /* not lint */ + +/* + * 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..8fd7a21ffa --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c @@ -0,0 +1,787 @@ +/* + * 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. + */ + +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: ctl_srvr.c,v 1.10 2008/11/14 02:36:51 marka Exp $"; +#endif /* not lint */ + +/* 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 <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..38dfdbe512 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c @@ -0,0 +1,369 @@ +/* + * 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] + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: ev_connects.c,v 1.8 2006/03/09 23:57:56 marka Exp $"; +#endif + +/* Import. */ + +#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..b12baf1aaa --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_files.c @@ -0,0 +1,277 @@ +/* + * 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] + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: ev_files.c,v 1.8 2005/07/28 06:51:48 marka Exp $"; +#endif + +#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..5dad36d04a --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c @@ -0,0 +1,308 @@ +/* + * 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] + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: ev_streams.c,v 1.5 2005/04/27 04:56:36 sra Exp $"; +#endif + +#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..12ac2cebca --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c @@ -0,0 +1,499 @@ +/* + * 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] + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: ev_timers.c,v 1.6 2005/04/27 04:56:36 sra Exp $"; +#endif + +/* Import. */ + +#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..99da1526c7 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c @@ -0,0 +1,247 @@ +/* + * 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] + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: ev_waits.c,v 1.4 2005/04/27 04:56:36 sra Exp $"; +#endif + +#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..be4a7848b9 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/eventlib.c @@ -0,0 +1,933 @@ +/* + * 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. + */ + +/* eventlib.c - implement glue for the eventlib + * vix 09sep95 [initial] + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: eventlib.c,v 1.10 2006/03/09 23:57:56 marka Exp $"; +#endif + +#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, *tvp; + sigset_t sigs; + int n; +#ifdef USE_POLL + int polltimeout = INFTIM; + evContext_p *ctx; + struct pollfd *fds; + nfds_t pnfds; + + UNUSED(nfds); +#endif /* USE_POLL */ + + if (tsp) { + tvp = &tv; + tv = evTimeVal(*tsp); +#ifdef USE_POLL + polltimeout = 1000 * tv.tv_sec + tv.tv_usec / 1000; +#endif /* USE_POLL */ + } else + tvp = NULL; + 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..3d22b6fc71 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/heap.c @@ -0,0 +1,236 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: heap.c,v 1.4 2006/03/09 23:57:56 marka Exp $"; +#endif /* not lint */ + +#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..8c2af2b9e3 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/logging.c @@ -0,0 +1,716 @@ +/* + * 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. + */ + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: logging.c,v 1.9 2008/11/14 02:36:51 marka Exp $"; +#endif /* not lint */ + +#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..515793fd6a --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/memcluster.c @@ -0,0 +1,588 @@ +/* + * 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 + + +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: memcluster.c,v 1.11 2006/08/30 23:34:38 marka Exp $"; +#endif /* not lint */ + +#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..8ba675fbe8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/isc/tree.c @@ -0,0 +1,534 @@ +#ifndef LINT +static const char rcsid[] = "$Id: tree.c,v 1.4 2005/04/27 04:56:39 sra Exp $"; +#endif + +/*% + * 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..292375af63 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c @@ -0,0 +1,129 @@ +/* + * 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 lint +static const char rcsid[] = "$Id: ns_date.c,v 1.6 2005/04/27 04:56:39 sra Exp $"; +#endif + +/* Import. */ + +#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..f6b0accef1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c @@ -0,0 +1,1153 @@ +/* + * 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 lint +static const char rcsid[] = "$Id: ns_name.c,v 1.11 2009/01/23 19:59:16 each Exp $"; +#endif + +#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..e196217f9b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c @@ -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 lint +static const char rcsid[] = "$Id: ns_netint.c,v 1.3 2005/04/27 04:56:40 sra Exp $"; +#endif + +/* Import. */ + +#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..c18dd060e1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c @@ -0,0 +1,273 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_newmsg.c,v 1.3 2009/02/26 10:48:57 marka Exp $"; +#endif + +#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..ba11eea707 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c @@ -0,0 +1,275 @@ +/* + * 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 lint +static const char rcsid[] = "$Id: ns_parse.c,v 1.10 2009/01/23 19:59:16 each Exp $"; +#endif + +/* Import. */ + +#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..e70df376c1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c @@ -0,0 +1,1242 @@ +/* + * 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 lint +static const char rcsid[] = "$Id: ns_print.c,v 1.12 2009/03/03 05:29:58 each Exp $"; +#endif + +/* Import. */ + +#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)); + } + } + + 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)); + } + } + + 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..eac4052278 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c @@ -0,0 +1,300 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_rdata.c,v 1.2 2009/01/23 23:49:15 tbox Exp $"; +#endif + +#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..5e9f5cab54 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c @@ -0,0 +1,207 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_samedomain.c,v 1.6 2005/04/27 04:56:40 sra Exp $"; +#endif + +#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..fc7dd19f7b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c @@ -0,0 +1,387 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_sign.c,v 1.6 2006/03/09 23:57:56 marka Exp $"; +#endif + +/* Import. */ + +#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..69c2f83f57 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c @@ -0,0 +1,162 @@ +/* + * 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 lint +static const char rcsid[] = "$Id: ns_ttl.c,v 1.4 2005/07/28 06:51:49 marka Exp $"; +#endif + +/* Import. */ + +#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; + case 'D': tmp *= 24; + case 'H': tmp *= 60; + case 'M': tmp *= 60; + 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..b4b63d4641 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c @@ -0,0 +1,484 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = "$Id: ns_verify.c,v 1.5 2006/03/09 23:57:56 marka Exp $"; +#endif + +/* Import. */ + +#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..568ce3ce68 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/herror.c @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)herror.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: herror.c,v 1.4 2005/04/27 04:56:41 sra Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..c82bf01eb8 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c @@ -0,0 +1,287 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_comp.c,v 1.5 2005/07/28 06:51:50 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..f1e0dd030b --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_data.c @@ -0,0 +1,322 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: res_data.c,v 1.7 2008/12/11 09:59:00 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 <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..e11fb29612 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c @@ -0,0 +1,1252 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_debug.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_debug.c,v 1.19 2009/02/26 11:20:20 tbox 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 <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..431c0262c1 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c @@ -0,0 +1,722 @@ +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: res_findzonecut.c,v 1.10 2005/10/11 00:10:16 marka Exp $"; +#endif /* not lint */ + +/* + * 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..10254d1931 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_init.c @@ -0,0 +1,958 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_init.c 8.1 (Berkeley) 6/7/93"; +static const char rcsid[] = "$Id: res_init.c,v 1.26 2008/12/11 09:59:00 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 <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..cf36855e9d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c @@ -0,0 +1,386 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_mkquery.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_mkquery.c,v 1.10 2008/12/11 09:59:00 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..8f73e281d0 --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c @@ -0,0 +1,1163 @@ +/* + * 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> + */ + +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: res_mkupdate.c,v 1.10 2008/12/11 09:59:00 marka Exp $"; +#endif /* not lint */ + +#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..09df3da1fc --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_query.c @@ -0,0 +1,440 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_query.c,v 1.11 2008/11/14 02:36:51 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#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..289db4e77d --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_send.c @@ -0,0 +1,1120 @@ +/* + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_send.c,v 1.22 2009/01/22 23:49:23 tbox Exp $"; +#endif /* LIBC_SCCS and not lint */ + +/*! \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..df24aee3bd --- /dev/null +++ b/usr/src/lib/libresolv2_joy/common/resolv/res_update.c @@ -0,0 +1,213 @@ +#if !defined(lint) && !defined(SABER) +static const char rcsid[] = "$Id: res_update.c,v 1.13 2005/04/27 04:56:43 sra Exp $"; +#endif /* not lint */ + +/* + * 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 c00a59dc5d..1940308f92 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. */ #ifndef _LIBSCF_H @@ -832,8 +833,13 @@ int smf_notify_del_params(const char *, const char *, int32_t); /* * SMF exit status definitions + * + * The SMF_EXIT_NODAEMON exit status should be used when a method does not + * need to run any persistent process. This indicates success, abandons the + * contract, and allows dependencies to be met. */ #define SMF_EXIT_OK 0 +#define SMF_EXIT_NODAEMON 94 #define SMF_EXIT_ERR_FATAL 95 #define SMF_EXIT_ERR_CONFIG 96 #define SMF_EXIT_MON_DEGRADE 97 diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c index 0282c621cb..4ac114ecdc 100644 --- a/usr/src/lib/libshare/nfs/libshare_nfs.c +++ b/usr/src/lib/libshare/nfs/libshare_nfs.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ /* @@ -2827,9 +2828,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 b584728d5f..1cce2316f0 100644 --- a/usr/src/lib/libshell/Makefile +++ b/usr/src/lib/libshell/Makefile @@ -64,6 +64,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 1feeec8a90..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 - sytems 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 0376912d1f..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/xpg4/bin/fgrep</member> - <member>/usr/bin/grep</member> <member>/usr/xpg4/bin/grep</member> - <member>/usr/bin/egrep</member><member>/usr/xpg4/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..914aab055b --- /dev/null +++ b/usr/src/lib/libsmartsshd/Makefile.com @@ -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 2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# + +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 -ldoor + +$(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..ce5fc7a9c0 --- /dev/null +++ b/usr/src/lib/libsmartsshd/common/mapfile-vers @@ -0,0 +1,45 @@ +# +# 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: + sshd_user_rsa_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..4f0f0bc1ad --- /dev/null +++ b/usr/src/lib/libsmartsshd/common/sshd-plugin.c @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOG_OOM(SZ) fprintf(stderr, "Unable to 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); + } + memset(door_args.rbuf, 0, RETURN_SZ); + + do { + fd = open(DOOR, O_RDWR); + if (fd < 0) + perror("smartplugin: open (of door FD) failed"); + + if (door_call(fd, &door_args) < 0) { + perror("smartplugin: door_call failed"); + } else { + allowed = atoi(door_args.rbuf); + munmap(door_args.rbuf, door_args.rsize); + return (allowed); + } + if (++attempts < MAX_ATTEMPTS) + sleep(SLEEP_PERIOD); + } while (attempts < MAX_ATTEMPTS); + + return (0); +} + +int +sshd_user_rsa_key_allowed(struct passwd *pw, RSA *key, const char *fp) +{ + return sshd_allowed_in_capi(pw, fp); +} + +int +sshd_user_dsa_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/libumem/amd64/umem_genasm.c b/usr/src/lib/libumem/amd64/umem_genasm.c index 00cc18ab67..ba68cb2d37 100644 --- a/usr/src/lib/libumem/amd64/umem_genasm.c +++ b/usr/src/lib/libumem/amd64/umem_genasm.c @@ -69,8 +69,6 @@ #include <umem_impl.h> #include "umem_base.h" -#include <stdio.h> - const int umem_genasm_supported = 1; static uintptr_t umem_genasm_mptr = (uintptr_t)&_malloc; static size_t umem_genasm_msize = 576; 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..3c6896de11 --- /dev/null +++ b/usr/src/lib/libvnd/Makefile.com @@ -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 (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../../Makefile.lib + +LIBRARY = libvnd.a +VERS = .1 +OBJECTS = libvnd.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc +CPPFLAGS += -I../common + +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/libwanboot/Makefile.com b/usr/src/lib/libwanboot/Makefile.com index 322bfb51f7..a98a30cfd4 100644 --- a/usr/src/lib/libwanboot/Makefile.com +++ b/usr/src/lib/libwanboot/Makefile.com @@ -60,7 +60,7 @@ include ../../Makefile.lib LIBS += $(LINTLIB) LDLIBS += -lnvpair -lresolv -lnsl -lsocket -ldevinfo -ldhcputil \ - -linetutil -lc -lcrypto -lssl + -linetutil -lc -lsunw_crypto -lsunw_ssl CPPFLAGS = -I$(SRC)/common/net/wanboot/crypt $(CPPFLAGS.master) CERRWARN += -_gcc=-Wno-switch CERRWARN += -_gcc=-Wno-parentheses 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..f2996b4e2d --- /dev/null +++ b/usr/src/lib/libzdoor/common/zdoor.c @@ -0,0 +1,437 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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; + zdoor_handle_t handle = NULL; + + 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); + + handle = entry->dte_parent->zte_parent; + + 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); + 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..25c67f62fb --- /dev/null +++ b/usr/src/lib/libzdoor/common/ztree.c @@ -0,0 +1,344 @@ +/* + * 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 <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; + + 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; + + 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 eb550f606f..ddb85646f7 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -64,6 +64,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 */ @@ -184,6 +185,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 e491a65c93..f240b6494f 100644 --- a/usr/src/lib/libzfs/common/libzfs_dataset.c +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c @@ -329,6 +329,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) { @@ -1988,6 +1992,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); @@ -2228,6 +2237,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen, 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 @@ -2238,6 +2248,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 ad4142da3f..9d98718dcb 100644 --- a/usr/src/lib/libzfs/common/libzfs_impl.h +++ b/usr/src/lib/libzfs/common/libzfs_impl.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. */ @@ -79,6 +80,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; }; #define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */ @@ -149,6 +151,7 @@ int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...); 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 *); +int libzfs_cmd_set_cachedprops(libzfs_handle_t *, zfs_cmd_t *); int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t, diff --git a/usr/src/lib/libzfs/common/libzfs_iter.c b/usr/src/lib/libzfs/common/libzfs_iter.c index 5fdfe1d591..01c059fe61 100644 --- a/usr/src/lib/libzfs/common/libzfs_iter.c +++ b/usr/src/lib/libzfs/common/libzfs_iter.c @@ -23,6 +23,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -110,6 +111,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); @@ -120,9 +125,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); @@ -147,15 +151,19 @@ zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data) 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); + 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) { if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl, - &zc)) == NULL) { + &zc)) == NULL) continue; - } if ((ret = func(nzhp, data)) != 0) { zcmd_free_nvlists(&zc); diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index 0418091025..0c37c36257 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -270,6 +270,12 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, case PROP_TYPE_NUMBER: intval = zpool_get_prop_int(zhp, prop, &src); + if (literal && prop != ZPOOL_PROP_HEALTH) { + (void) snprintf(buf, len, "%llu", + (u_longlong_t)intval); + break; + } + switch (prop) { case ZPOOL_PROP_SIZE: case ZPOOL_PROP_ALLOCATED: @@ -334,6 +340,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: diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index 507a72ce02..59b978cf16 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -83,6 +83,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")); @@ -611,6 +614,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) { @@ -646,6 +685,8 @@ libzfs_init(void) zpool_feature_init(); libzfs_mnttab_init(hdl); + hdl->libzfs_cachedprops = B_FALSE; + return (hdl); } diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index c46260717e..9ed5c461c0 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 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2011, 2014 by Delphix. All rights reserved. +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # # MAPFILE HEADER START # @@ -59,6 +60,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; @@ -104,6 +106,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/libzonecfg/common/getzoneent.c b/usr/src/lib/libzonecfg/common/getzoneent.c index 8155f7272a..76664fcc92 100644 --- a/usr/src/lib/libzonecfg/common/getzoneent.c +++ b/usr/src/lib/libzonecfg/common/getzoneent.c @@ -403,14 +403,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 +411,12 @@ 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; + } break; case PZE_REMOVE: diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index 6ae73b9f3b..3162390931 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.c @@ -22,6 +22,7 @@ /* * Copyright 2014 Gary Mills * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015, Joyent Inc. All rights reserved. */ #include <libsysevent.h> @@ -78,6 +79,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" @@ -85,6 +88,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" @@ -104,10 +108,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" @@ -117,6 +123,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" @@ -129,6 +136,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" @@ -175,9 +183,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} }; @@ -1271,7 +1282,6 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename) /* * Try to restore from our backup. */ - (void) unlink(filename); (void) rename(bakfile, filename); } else { /* @@ -2065,6 +2075,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 != NULL || 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 @@ -2083,13 +2119,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; @@ -2101,17 +2142,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) @@ -2138,6 +2182,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 || @@ -2180,6 +2237,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) @@ -2190,13 +2262,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); @@ -2212,13 +2311,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); } @@ -2243,7 +2369,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)) @@ -2255,8 +2382,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); @@ -2305,6 +2437,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. */ @@ -2454,7 +2638,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]; @@ -2499,13 +2683,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); @@ -2514,6 +2725,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); } @@ -4573,7 +4799,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) @@ -4594,15 +4820,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); @@ -4711,7 +4945,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) @@ -4747,6 +4982,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) { @@ -4754,6 +5007,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); } @@ -4773,7 +5049,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) @@ -4796,6 +5072,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); } @@ -5524,6 +5825,164 @@ 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 buf[80]; + + if ((fd = open(DEBUGID_FILE, 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. @@ -5806,6 +6265,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 @@ -6839,131 +7322,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); -} - -int -zonecfg_delete_mcap(zone_dochandle_t handle) -{ - int err; - xmlNodePtr cur = handle->zone_dh_cur; - - if ((err = operation_prep(handle)) != Z_OK) - return (err); - - for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { - if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0) - continue; - - xmlUnlinkNode(cur); - xmlFreeNode(cur); - return (Z_OK); - } - 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); -} - +/* + * Cleanup obsolete constructs in the configuration. + * Return true of the config has been updated and must be commited. + */ int -zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr) +zonecfg_fix_obsolete(zone_dochandle_t handle) { + int res = 0; + int add_physmem_rctl = 0; xmlNodePtr cur; - int err; - - if (tabptr == NULL) - return (Z_INVAL); + 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 ((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); + + if (fetchprop(cur, DTD_ATTR_PHYSCAP, + zone_physmem_cap, sizeof (zone_physmem_cap)) == Z_OK) { + res = 1; + add_physmem_rctl = 1; } - return (Z_OK); + xmlUnlinkNode(cur); + xmlFreeNode(cur); + break; } - return (Z_NO_ENTRY); -} - -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 (add_physmem_rctl) { + uint64_t cap; + char *endp; - 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); + cap = strtoull(zone_physmem_cap, &endp, 10); + (void) zonecfg_set_aliased_rctl(handle, ALIAS_MAXPHYSMEM, cap); } - 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); + return (res); } /* diff --git a/usr/src/lib/libzonecfg/common/mapfile-vers b/usr/src/lib/libzonecfg/common/mapfile-vers index b908a28174..7265b06a1f 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 (c) 2011, Joyent Inc. All rights reserved. # # @@ -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; @@ -79,7 +81,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; @@ -104,7 +105,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; @@ -118,6 +121,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; @@ -127,7 +131,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; @@ -160,7 +163,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; @@ -169,7 +171,6 @@ 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; @@ -183,6 +184,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; @@ -196,6 +198,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zonecfg_set_autoboot; zonecfg_set_bootargs; zonecfg_set_brand; + zonecfg_set_did; zonecfg_setdevent; zonecfg_setdevperment; zonecfg_setdsent; @@ -211,6 +214,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; @@ -229,6 +233,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 d94bb09c5f..3c7198efef 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> @@ -156,6 +164,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/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h index b1154fb71b..ad27a00026 100644 --- a/usr/src/lib/libzpool/common/sys/zfs_context.h +++ b/usr/src/lib/libzpool/common/sys/zfs_context.h @@ -22,7 +22,7 @@ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2012, 2014 by Delphix. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ #ifndef _SYS_ZFS_CONTEXT_H @@ -305,6 +305,7 @@ extern void kstat_runq_back_to_waitq(kstat_io_t *); #define KM_SLEEP UMEM_NOFAIL #define KM_PUSHPAGE KM_SLEEP #define KM_NOSLEEP UMEM_DEFAULT +#define KM_NORMALPRI 0 #define KMC_NODEBUG UMC_NODEBUG #define KMC_NOTOUCH 0 /* not needed for userland caches */ #define kmem_alloc(_s, _f) umem_alloc(_s, _f) diff --git a/usr/src/lib/nsswitch/dns/Makefile.com b/usr/src/lib/nsswitch/dns/Makefile.com index 60716d843d..0366633c0c 100644 --- a/usr/src/lib/nsswitch/dns/Makefile.com +++ b/usr/src/lib/nsswitch/dns/Makefile.com @@ -44,5 +44,5 @@ CPPFLAGS += -DNSS_DNS_LIBRESOLV=\"libresolv.so.2\" LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2 -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/pkcs11/pkcs11_tpm/Makefile.com b/usr/src/lib/pkcs11/pkcs11_tpm/Makefile.com index 020051c977..7345ddc892 100644 --- a/usr/src/lib/pkcs11/pkcs11_tpm/Makefile.com +++ b/usr/src/lib/pkcs11/pkcs11_tpm/Makefile.com @@ -73,7 +73,7 @@ TSSLIB=-L$(TSPILIBDIR) TSSLIB64=-L$(TSPILIBDIR)/$(MACH64) TSSINC=-I$(TSPIINCDIR) -LDLIBS += $(TSSLIB) -L$(ADJUNCT_PROTO)/lib -lc -luuid -lmd -ltspi -lcrypto +LDLIBS += $(TSSLIB) -L$(ADJUNCT_PROTO)/lib -lc -luuid -lmd -ltspi -lsunw_crypto CPPFLAGS += -xCC -D_POSIX_PTHREAD_SEMANTICS $(TSSINC) CPPFLAGS64 += $(CPPFLAGS) C99MODE= $(C99_ENABLE) diff --git a/usr/src/lib/pysolaris/Makefile.com b/usr/src/lib/pysolaris/Makefile.com index 28d68704f9..a6194a9a2a 100644 --- a/usr/src/lib/pysolaris/Makefile.com +++ b/usr/src/lib/pysolaris/Makefile.com @@ -44,6 +44,7 @@ C99LMODE= -Xc99=%all LIBS = $(DYNLIB) LDLIBS += -lc -lsec -lidmap -lpython2.6 CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include/python2.6 CERRWARN += -_gcc=-Wno-unused-variable CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include/python2.6 diff --git a/usr/src/lib/varpd/Makefile b/usr/src/lib/varpd/Makefile new file mode 100644 index 0000000000..daa849572e --- /dev/null +++ b/usr/src/lib/varpd/Makefile @@ -0,0 +1,33 @@ +# +# 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. +# + +SUBDIRS = libvarpd .WAIT 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) + +$(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..c071405972 --- /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 2015 Joyent, Inc. +# + +LIBRARY = libvarpd_direct.a +VERS = .1 +OBJECTS = libvarpd_direct.o + +include ../../../Makefile.lib +include ../../Makefile.plugin + +LIBS = $(DYNLIB) +LDLIBS += -lc -lvarpd -lumem -lnvpair -lnsl +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..235a73c804 --- /dev/null +++ b/usr/src/lib/varpd/direct/common/libvarpd_direct.c @@ -0,0 +1,409 @@ +/* + * 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. + */ + +/* + * 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, 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; + + (void) mutex_lock(&vdp->vad_lock); + if (vdp->vad_hip == B_FALSE ||((vdp->vad_dest & OVERLAY_PLUGIN_D_IP) && + vdp->vad_hport == B_FALSE)) { + (void) mutex_unlock(&vdp->vad_lock); + return (EAGAIN); + } + (void) mutex_unlock(&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; + + (void) mutex_lock(&vdp->vad_lock); + bcopy(&vdp->vad_ip, &otp->otp_ip, sizeof (struct in6_addr)); + otp->otp_port = vdp->vad_port; + (void) mutex_unlock(&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); + (void) mutex_lock(&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); + } + (void) mutex_unlock(&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); + (void) mutex_lock(&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); + } + (void) mutex_unlock(&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); + + (void) mutex_lock(&vdp->vad_lock); + bcopy(buf, &vdp->vad_ip, sizeof (struct in6_addr)); + vdp->vad_hip = B_TRUE; + (void) mutex_unlock(&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); + + (void) mutex_lock(&vdp->vad_lock); + vdp->vad_port = (uint16_t)*valp; + vdp->vad_hport = B_TRUE; + (void) mutex_unlock(&vdp->vad_lock); + return (0); + } + + return (EINVAL); +} + +static int +varpd_direct_save(void *arg, nvlist_t *nvp) +{ + int ret; + varpd_direct_t *vdp = arg; + + (void) mutex_lock(&vdp->vad_lock); + if (vdp->vad_hport == B_TRUE) { + if ((ret = nvlist_add_uint16(nvp, varpd_direct_props[1], + vdp->vad_port)) != 0) { + (void) mutex_unlock(&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) { + (void) mutex_unlock(&vdp->vad_lock); + return (ret); + } + } + (void) mutex_unlock(&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, 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..e68797246f --- /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 2015 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 -lvarpd -lumem -lnvpair -lsocket -lnsl -lcmdutils +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..66aaefb29b --- /dev/null +++ b/usr/src/lib/varpd/files/common/libvarpd_files_json.c @@ -0,0 +1,940 @@ +/* + * 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; + } + + if ((str = custr_cstr(s->s_collect)) != NULL) { + abort(); + } + 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 __GNU_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 and ensure it is backed by at + * least some string memory. + */ + if (custr_alloc(&s.s_collect) != 0 || + custr_append(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..27506e22d6 --- /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 2015 Joyent, Inc. + */ + +#ifndef _LIBVARPD_FILES_JSON_H +#define _LIBVARPD_FILES_JSON_H + +#include <libnvpair.h> +#include <libcmdutils.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..62bf9cc14c --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd.c @@ -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. + */ + +/* + * 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, 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; + + 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, NULL) != 0) + libvarpd_panic("failed to create mutex: %d", errno); + + (void) mutex_lock(&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); + (void) mutex_unlock(&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; + (void) mutex_lock(&vip->vdi_lock); + retp = avl_find(&vip->vdi_instances, &lookup, NULL); + (void) mutex_unlock(&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; + (void) mutex_lock(&vip->vdi_lock); + retp = avl_find(&vip->vdi_linstances, &lookup, NULL); + (void) mutex_unlock(&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. + */ + (void) mutex_lock(&vip->vdi_lock); + avl_remove(&vip->vdi_instances, inst); + avl_remove(&vip->vdi_linstances, inst); + (void) mutex_unlock(&vip->vdi_lock); + + (void) mutex_lock(&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; + } + (void) mutex_unlock(&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; + + (void) mutex_lock(&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: + (void) mutex_unlock(&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..a16c5b8cf7 --- /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[1500]; + 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..b0fa907386 --- /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 2015 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_handle_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 == NULL || 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..5c6610e439 --- /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; + + (void) mutex_lock(&vip->vdi_lock); + if (vip->vdi_doorfd >= 0) { + (void) mutex_unlock(&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) { + (void) mutex_unlock(&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); + (void) mutex_unlock(&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); + (void) mutex_unlock(&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); + (void) mutex_unlock(&vip->vdi_lock); + return (ret); + } + + (void) mutex_unlock(&vip->vdi_lock); + return (0); +} + +void +libvarpd_door_server_destroy(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + (void) mutex_lock(&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; + } + (void) mutex_unlock(&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..7ecd3a952f --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h @@ -0,0 +1,247 @@ +/* + * 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; +} 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; + 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..c7c69da7be --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c @@ -0,0 +1,579 @@ +/* + * 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; + + (void) mutex_lock(&vip->vdi_lock); + if (vip->vdi_lthr_quiesce == B_TRUE) { + (void) mutex_unlock(&vip->vdi_lock); + return; + } + vip->vdi_lthr_count++; + + for (;;) { + (void) mutex_unlock(&vip->vdi_lock); + libvarpd_overlay_lookup_handle(vip); + (void) mutex_lock(&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); + (void) mutex_unlock(&vip->vdi_lock); +} + +void +libvarpd_overlay_lookup_quiesce(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + (void) mutex_lock(&vip->vdi_lock); + if (vip->vdi_lthr_count == 0) { + (void) mutex_unlock(&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; + (void) mutex_unlock(&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); +} + +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; + + 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..b48a184682 --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_persist.c @@ -0,0 +1,584 @@ +/* + * 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 <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); + } + + + (void) rw_wrlock(&vip->vdi_pfdlock); + if (vip->vdi_persistfd != -1) { + (void) 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; + (void) 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; + + (void) 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); + (void) rw_unlock(&vip->vdi_pfdlock); + return (err); +} + +void +libvarpd_torch_instance(varpd_impl_t *vip, varpd_instance_t *inst) +{ + char buf[32]; + int ret; + + (void) rw_rdlock(&vip->vdi_pfdlock); + if (vip->vdi_persistfd == -1) { + (void) 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)); + } + } + + (void) 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, NULL) != 0) + libvarpd_panic("failed to create vri_lock mutex"); + + (void) mutex_lock(&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); + (void) mutex_unlock(&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); + } + } + + (void) mutex_lock(&inst->vri_lock); + inst->vri_flags |= VARPD_INSTANCE_F_ACTIVATED; + (void) mutex_unlock(&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; + + (void) mutex_lock(&vip->vdi_lock); + for (inst = avl_first(&vip->vdi_instances); inst != NULL; + inst = AVL_NEXT(&vip->vdi_instances, inst)) { + if (inst->vri_linkid == linkid) { + (void) mutex_unlock(&vip->vdi_lock); + return (0); + } + } + + (void) mutex_unlock(&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; + + (void) 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); + (void) rw_unlock(&vip->vdi_pfdlock); + return (ret); +} + +int +libvarpd_persist_disable(varpd_handle_t *vhp) +{ + varpd_impl_t *vip = (varpd_impl_t *)vhp; + + (void) rw_wrlock(&vip->vdi_pfdlock); + if (vip->vdi_persistfd == -1) { + (void) mutex_unlock(&vip->vdi_lock); + (void) 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; + (void) 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..100788b64d --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c @@ -0,0 +1,268 @@ +/* + * 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); + } + + (void) mutex_lock(&varpd_load_lock); + if (varpd_load_handle == NULL) + libvarpd_panic("varpd_load_handle was unexpectedly null"); + + (void) mutex_lock(&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); + (void) mutex_unlock(&varpd_load_handle->vdi_lock); + (void) mutex_unlock(&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); + (void) mutex_unlock(&varpd_load_handle->vdi_lock); + (void) mutex_unlock(&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, NULL) != 0) + libvarpd_panic("failed to create plugin's vpp_lock"); + vpp->vpp_active = 0; + avl_add(&varpd_load_handle->vdi_plugins, vpp); + (void) mutex_unlock(&varpd_load_handle->vdi_lock); + (void) mutex_unlock(&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; + (void) mutex_lock(&vip->vdi_lock); + ret = avl_find(&vip->vdi_plugins, &lookup, NULL); + (void) mutex_unlock(&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); + (void) mutex_lock(&varpd_load_lock); + while (varpd_load_handle != NULL) + (void) cond_wait(&varpd_load_cv, &varpd_load_lock); + varpd_load_handle = vip; + (void) mutex_unlock(&varpd_load_lock); + + ret = libvarpd_dirwalk(vip, path, ".so", libvarpd_plugin_load_cb, NULL); + + (void) mutex_lock(&varpd_load_lock); + varpd_load_handle = NULL; + (void) cond_signal(&varpd_load_cv); + (void) mutex_unlock(&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; + + (void) mutex_lock(&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) { + (void) mutex_unlock(&vip->vdi_lock); + return (1); + } + } + (void) mutex_unlock(&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) +{ + (void) mutex_lock(&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); + (void) mutex_unlock(&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..abe65a8c5d --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_prop.c @@ -0,0 +1,299 @@ +/* + * 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 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); + 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..c0d463968d --- /dev/null +++ b/usr/src/lib/varpd/libvarpd/common/mapfile-plugin @@ -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 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_plugin_alloc { 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_register { 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..275f07bf8b --- /dev/null +++ b/usr/src/lib/varpd/svp/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/svp/Makefile.com b/usr/src/lib/varpd/svp/Makefile.com new file mode 100644 index 0000000000..41677b2e50 --- /dev/null +++ b/usr/src/lib/varpd/svp/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_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_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 -lvarpd -lumem -lnvpair -lsocket -lnsl -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..d1764912f3 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp.c @@ -0,0 +1,1066 @@ +/* + * 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_LOG_REQ, 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 | + * +------------+ + * + * ------------ + * 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. + */ + +#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" +}; + +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; + + if (IN6_IS_ADDR_V4MAPPED(vl3ip) == 0) + libvarpd_panic("implement libvarpd_inject_ndp"); + IN6_V4MAPPED_TO_INADDR(vl3ip, &v4); + 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, 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; + + (void) mutex_lock(&svp->svp_lock); + if (svp->svp_host == NULL || svp->svp_port == 0 || + svp->svp_huip == B_FALSE || svp->svp_uport == 0) { + (void) mutex_unlock(&svp->svp_lock); + return (EAGAIN); + } + (void) mutex_unlock(&svp->svp_lock); + + if ((ret = svp_remote_find(svp->svp_host, svp->svp_port, &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; + static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + /* + * 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, 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; + + (void) mutex_lock(&svp->svp_lock); + if (svp->svp_host == NULL) { + *sizep = 0; + } else { + len = strlen(svp->svp_host) + 1; + if (*sizep < len) { + (void) mutex_unlock(&svp->svp_lock); + return (EOVERFLOW); + } + *sizep = len; + (void) strlcpy(buf, svp->svp_host, *sizep); + } + (void) mutex_unlock(&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); + + (void) mutex_lock(&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); + } + + (void) mutex_unlock(&svp->svp_lock); + return (0); + } + + /* svp/underlay_ip */ + if (strcmp(pname, varpd_svp_props[2]) == 0) { + if (*sizep > sizeof (struct in6_addr)) + return (EOVERFLOW); + (void) mutex_lock(&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); + + (void) mutex_lock(&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); + } + + (void) mutex_unlock(&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); + (void) mutex_lock(&svp->svp_lock); + if (svp->svp_host != NULL) + umem_free(svp->svp_host, strlen(svp->svp_host) + 1); + svp->svp_host = dup; + (void) mutex_unlock(&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); + + (void) mutex_lock(&svp->svp_lock); + svp->svp_port = (uint16_t)*valp; + (void) mutex_unlock(&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); + } + + (void) mutex_lock(&svp->svp_lock); + bcopy(buf, &svp->svp_uip, sizeof (struct in6_addr)); + svp->svp_huip = B_TRUE; + (void) mutex_unlock(&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); + + (void) mutex_lock(&svp->svp_lock); + svp->svp_uport = (uint16_t)*valp; + (void) mutex_unlock(&svp->svp_lock); + + return (0); + } + + return (EINVAL); +} + +static int +varpd_svp_save(void *arg, nvlist_t *nvp) +{ + int ret; + svp_t *svp = arg; + + (void) mutex_lock(&svp->svp_lock); + if (svp->svp_host != NULL) { + if ((ret = nvlist_add_string(nvp, varpd_svp_props[0], + svp->svp_host)) != 0) { + (void) mutex_unlock(&svp->svp_lock); + return (ret); + } + } + + if (svp->svp_port != 0) { + if ((ret = nvlist_add_uint16(nvp, varpd_svp_props[1], + svp->svp_port)) != 0) { + (void) mutex_unlock(&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) { + (void) mutex_unlock(&svp->svp_lock); + return (ret); + } + } + + if (svp->svp_uport != 0) { + if ((ret = nvlist_add_uint16(nvp, varpd_svp_props[3], + svp->svp_uport)) != 0) { + (void) mutex_unlock(&svp->svp_lock); + return (ret); + } + } + + (void) mutex_unlock(&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..a9b68bcc4e --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp.h @@ -0,0 +1,307 @@ +/* + * 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; + void *st_arg; + boolean_t st_oneshot; + uint32_t st_value; + /* Fields below here are private to the svp_timer implementaiton */ + uint64_t st_expire; + boolean_t st_delivering; + avl_node_t st_link; +} svp_timer_t; + +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_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; + svp_query_f sq_func; + svp_query_state_t sq_state; + void *sq_arg; + svp_t *sq_svp; + svp_req_t sq_header; + svp_query_data_t sq_rdun; + svp_query_data_t sq_wdun; + svp_status_t sq_status; + void *sq_rdata; + size_t sq_rsize; + void *sq_wdata; + size_t sq_wsize; + hrtime_t sq_acttime; +}; + +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; + hrtime_t sc_lastact; + 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; + +struct svp_remote { + char *sr_hostname; /* RO */ + uint16_t sr_rport; /* 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 */ +}; + +/* + * 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, 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(void *); + +/* + * 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); + +#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..85786d6dd9 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c @@ -0,0 +1,960 @@ +/* + * 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 <libvarpd_svp.h> + +static 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)) != 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); + } + } + + 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) { + (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))) { + (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); + } + + scp->sc_input.sci_query = sqp; + sqp->sq_wdata = &sqp->sq_wdun; + sqp->sq_wsize = sizeof (svp_query_data_t); + + 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); + 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 { + libvarpd_panic("unhandled nop: %d", nop); + } + + list_remove(&scp->sc_queries, sqp); + (void) mutex_unlock(&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); + (void) mutex_lock(&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_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; + + (void) mutex_lock(&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) { + (void) mutex_unlock(&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) { + (void) mutex_unlock(&scp->sc_lock); + return; + } + } + + /* Check if this needs to be freed */ + if (scp->sc_flags & SVP_CF_REAP) { + (void) mutex_unlock(&scp->sc_lock); + svp_conn_destroy(scp); + return; + } + + /* Check if this needs to be reset */ + if (scp->sc_flags & SVP_CF_TEARDOWN) { + 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: + (void) mutex_unlock(&scp->sc_lock); + + if (ret == SVP_RA_NONE) + return; + + (void) mutex_lock(&srp->sr_lock); + (void) mutex_lock(&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); + } + (void) mutex_unlock(&scp->sc_lock); + (void) mutex_unlock(&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) +{ + svp_query_t *sqp; + svp_conn_t *scp = arg; + hrtime_t now = gethrtime(); + + (void) mutex_lock(&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) { + (void) mutex_unlock(&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 ((sqp->sq_acttime - now) / NANOSEC > svp_conn_query_timeout) + break; + } + + /* Nothing timed out, we're good here */ + if (sqp == NULL) { + (void) mutex_unlock(&scp->sc_lock); + return; + } + + scp->sc_flags |= SVP_CF_TEARDOWN; + svp_conn_inject(scp); + + (void) mutex_unlock(&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)); + + (void) mutex_lock(&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"); + } + (void) mutex_unlock(&scp->sc_lock); + (void) mutex_unlock(&srp->sr_lock); +} + +int +svp_conn_create(svp_remote_t *srp, const struct in6_addr *addr) +{ + svp_conn_t *scp; + + assert(MUTEX_HELD(&srp->sr_lock)); + scp = umem_zalloc(sizeof (svp_conn_t), UMEM_DEFAULT); + if (scp == NULL) + return (ENOMEM); + + 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; + (void) mutex_lock(&scp->sc_lock); + svp_conn_add(scp); + (void) mutex_unlock(&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; + + (void) mutex_lock(&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); + } + (void) mutex_unlock(&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..8755730359 --- /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 = DEFAULTMUTEX; +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; + + (void) mutex_lock(&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); + (void) mutex_unlock(&svp_host_lock); + + (void) mutex_lock(&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) { + (void) mutex_unlock(&srp->sr_lock); + continue; + } + srp->sr_state |= SVP_RS_LOOKUP_INPROGRESS; + (void) mutex_unlock(&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. + */ + (void) mutex_lock(&srp->sr_lock); + svp_remote_degrade(srp, + SVP_RD_DNS_FAIL); + (void) mutex_unlock(&srp->sr_lock); + break; + } + } + break; + } + + if (err == 0) { + /* + * We've successfully resolved something, mark this + * degredation over for now. + */ + (void) mutex_lock(&srp->sr_lock); + svp_remote_restore(srp, SVP_RD_DNS_FAIL); + (void) mutex_unlock(&srp->sr_lock); + svp_remote_resolved(srp, addrs); + } + + (void) mutex_lock(&srp->sr_lock); + srp->sr_state &= ~SVP_RS_LOOKUP_INPROGRESS; + (void) cond_broadcast(&srp->sr_cond); + (void) mutex_unlock(&srp->sr_lock); + } + + /* LINTED: E_STMT_NOT_REACHED */ + return (NULL); +} + +void +svp_host_queue(svp_remote_t *srp) +{ + svp_remote_t *s; + (void) mutex_lock(&svp_host_lock); + (void) mutex_lock(&srp->sr_lock); + if (srp->sr_state & SVP_RS_LOOKUP_SCHEDULED) { + (void) mutex_unlock(&srp->sr_lock); + (void) mutex_unlock(&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); + (void) mutex_unlock(&srp->sr_lock); + (void) mutex_unlock(&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..20cbb6540f --- /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 = DEFAULTMUTEX; + +/* ARGSUSED */ +static void * +svp_event_thr(void *arg) +{ + for (;;) { + int ret; + port_event_t pe; + svp_event_t *sep; + + (void) mutex_lock(&svp_elock); + if (svp_event.sel_stop == B_TRUE) { + (void) mutex_unlock(&svp_elock); + break; + } + (void) mutex_unlock(&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(void *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) +{ + (void) mutex_lock(&svp_elock); + svp_event.sel_stop = B_TRUE; + (void) mutex_unlock(&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..7147429de3 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h @@ -0,0 +1,239 @@ +/* + * 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_RACK = 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_S_BADLOG = 0x05, /* Unknown svp_log_type_t */ + SVP_S_LOGAGAIN = 0x06 /* Nothing in the log yet */ +} 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 entries that the client is prepared to receive are in + * svlr_count. However, the client may receive less than they asked for. + */ +typedef enum svp_log_type { + SVP_LOG_VL2 = 0x01, + SVP_LOG_VL3 = 0x02 +} svp_log_type_t; + +typedef struct svp_log_req { + uint32_t svlr_type; + uint32_t svlr_count; +} svp_log_req_t; + +/* + * The server replies to a log request by sending a series of log entries based + * on the type of svp_log_type_t in the SVP_R_LOG_ACK. If it's a VL2 request, + * then the svp_log_vl2_t is used, otherwise the svp_log_vl3_t is used. The + * response always leads with a svp_bulk_ack_t. It is then followed by a number + * of entries which can be calculated based on taking the toal data payload, + * subtracting the svp_log_ack_t, and then dividing that by the size of the + * corresponding data structure. + */ +typedef struct 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 { + uint8_t svl3_id[16]; /* 16-byte UUID */ + uint8_t slv3_ip[16]; + uint8_t svl3_mac[ETHERADDRL]; + uint16_t svl3_vlan; + uint8_t svl3_tmac[ETHERADDRL]; + uint8_t svl3_tpad[2]; + uint32_t svl3_vnetid; +} svp_log_vl3_t; + +typedef struct svp_log_ack { + uint32_t svla_status; + uint32_t svla_type; + 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. Again, the member svrr_type should be a svp_log_type_t + * member. + */ +typedef struct svp_lrm_req { + uint32_t svrr_type; + uint32_t svrr_pad; + 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..86a80e95d4 --- /dev/null +++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.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. + */ + +/* + * 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> + +static mutex_t svp_remote_lock = DEFAULTMUTEX; +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); + else + return (0); +} + +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. + */ + (void) mutex_lock(&srp->sr_lock); + while (srp->sr_state & + (SVP_RS_LOOKUP_SCHEDULED | SVP_RS_LOOKUP_INPROGRESS)) { + (void) cond_wait(&srp->sr_cond, &srp->sr_lock); + } + (void) mutex_unlock(&srp->sr_lock); + + if (cond_destroy(&srp->sr_cond) != 0) + libvarpd_panic("faile 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, 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) { + (void) mutex_unlock(&svp_remote_lock); + return (ENOMEM); + } + hlen = strlen(host) + 1; + remote->sr_hostname = umem_alloc(hlen, UMEM_DEFAULT); + if (remote->sr_hostname == NULL) { + umem_free(remote, sizeof (svp_remote_t)); + (void) mutex_unlock(&svp_remote_lock); + return (ENOMEM); + } + remote->sr_rport = port; + if (mutex_init(&remote->sr_lock, USYNC_THREAD, 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; + + *outp = remote; + return (0); +} + +int +svp_remote_find(char *host, uint16_t port, svp_remote_t **outp) +{ + int ret; + svp_remote_t lookup, *remote; + + lookup.sr_hostname = host; + lookup.sr_rport = port; + (void) mutex_lock(&svp_remote_lock); + remote = avl_find(&svp_remote_tree, &lookup, NULL); + if (remote != NULL) { + assert(remote->sr_count > 0); + remote->sr_count++; + *outp = remote; + (void) mutex_unlock(&svp_remote_lock); + return (0); + } + + if ((ret = svp_remote_create(host, port, outp)) != 0) { + (void) mutex_unlock(&svp_remote_lock); + return (ret); + } + + avl_add(&svp_remote_tree, *outp); + (void) mutex_unlock(&svp_remote_lock); + + /* Make sure DNS is up to date */ + svp_host_queue(*outp); + + return (0); +} + +void +svp_remote_release(svp_remote_t *srp) +{ + (void) mutex_lock(&svp_remote_lock); + (void) mutex_lock(&srp->sr_lock); + srp->sr_count--; + if (srp->sr_count != 0) { + (void) mutex_unlock(&srp->sr_lock); + (void) mutex_unlock(&svp_remote_lock); + return; + } + (void) mutex_unlock(&srp->sr_lock); + + avl_remove(&svp_remote_tree, srp); + (void) mutex_unlock(&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; + + (void) mutex_lock(&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; + (void) mutex_unlock(&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"); + + (void) mutex_lock(&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; + (void) mutex_unlock(&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)) { + (void) mutex_lock(&scp->sc_lock); + if (scp->sc_cstate != SVP_CS_ACTIVE) { + (void) mutex_unlock(&scp->sc_lock); + continue; + } + svp_conn_queue(scp, sqp); + (void) mutex_unlock(&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); + + (void) mutex_lock(&srp->sr_lock); + if (svp_remote_conn_queue(srp, sqp) == B_FALSE) + svp->svp_cb.scb_vl2_lookup(svp, SVP_S_FATAL, NULL, NULL, arg); + (void) mutex_unlock(&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); +} + +void +svp_remote_vl3_lookup(svp_t *svp, svp_query_t *sqp, + const struct sockaddr *addr, void *arg) +{ + svp_remote_t *srp; + 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"); + + srp = svp->svp_remote; + sqp->sq_func = svp_remote_vl3_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_VL3_REQ); + sqp->sq_header.svp_size = htons(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 = 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 = SVP_VL3_IP; + IN6_INADDR_TO_V4MAPPED(&s4->sin_addr, &v6); + bcopy(&v6, vl3r->sl3r_ip, sizeof (struct in6_addr)); + } + vl3r->sl3r_vnetid = ntohl(svp->svp_vid); + + (void) mutex_lock(&srp->sr_lock); + if (svp_remote_conn_queue(srp, sqp) == B_FALSE) + svp->svp_cb.scb_vl3_lookup(svp, SVP_S_FATAL, NULL, NULL, NULL, + arg); + (void) mutex_unlock(&srp->sr_lock); +} + +/* ARGSUSED */ +void +svp_remote_dns_timer(void *unused) +{ + svp_remote_t *s; + (void) mutex_lock(&svp_remote_lock); + for (s = avl_first(&svp_remote_tree); s != NULL; + s = AVL_NEXT(&svp_remote_tree, s)) { + svp_host_queue(s); + } + (void) mutex_unlock(&svp_remote_lock); +} + +void +svp_remote_resolved(svp_remote_t *srp, struct addrinfo *newaddrs) +{ + struct addrinfo *a; + svp_conn_t *scp; + int ngen; + + (void) mutex_lock(&srp->sr_lock); + srp->sr_gen++; + ngen = srp->sr_gen; + (void) mutex_unlock(&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; + } + + (void) mutex_lock(&srp->sr_lock); + for (scp = list_head(&srp->sr_conns); scp != NULL; + scp = list_next(&srp->sr_conns, scp)) { + (void) mutex_lock(&scp->sc_lock); + if (bcmp(addrp, &scp->sc_addr, + sizeof (struct in6_addr)) == 0) { + scp->sc_gen = ngen; + (void) mutex_unlock(&scp->sc_lock); + break; + } + (void) mutex_unlock(&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); + (void) mutex_unlock(&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. + */ + (void) mutex_lock(&srp->sr_lock); + for (scp = list_head(&srp->sr_conns); scp != NULL; + scp = list_next(&srp->sr_conns, scp)) { + boolean_t fall = B_FALSE; + (void) mutex_lock(&scp->sc_lock); + if (scp->sc_gen < srp->sr_gen) + fall = B_TRUE; + (void) mutex_unlock(&scp->sc_lock); + if (fall == B_TRUE) + svp_conn_fallout(scp); + } + (void) mutex_unlock(&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. + */ +void +svp_remote_reassign(svp_remote_t *srp, svp_conn_t *scp) +{ + assert(MUTEX_HELD(&srp->sr_lock)); + svp_query_t *sqp; + + /* + * As we try to reassing all of its queries, remove it from the list. + */ + list_remove(&srp->sr_conns, scp); + + while ((sqp = list_remove_head(&scp->sc_queries)) != NULL) { + sqp->sq_wdata = NULL; + sqp->sq_wsize = 0; + 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); + } + } +} + +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_timer.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c new file mode 100644 index 0000000000..b4c0d754b4 --- /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 = DEFAULTMUTEX; +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) +{ + (void) mutex_lock(&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; + (void) mutex_unlock(&svp_timer_lock); + t->st_func(t->st_arg); + (void) mutex_lock(&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); + } + } + (void) mutex_unlock(&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"); + + (void) mutex_lock(&svp_timer_lock); + stp->st_delivering = B_FALSE; + stp->st_expire = svp_timer_nticks + stp->st_value; + avl_add(&svp_timer_tree, stp); + (void) mutex_unlock(&svp_timer_lock); +} + +void +svp_timer_remove(svp_timer_t *stp) +{ + (void) mutex_lock(&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); + (void) mutex_unlock(&svp_timer_lock); + return; + } + + stp->st_oneshot = B_TRUE; + while (stp->st_delivering == B_TRUE) + (void) cond_wait(&svp_timer_cv, &svp_timer_lock); + + (void) mutex_unlock(&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) |