Hi Seth,

It is a known issue, I actually had pulled the code for inet_net_pton()
from NetBSD many months ago, when I first realized inet6 masks were not
working in smtpd, but then I got sucked into other stuff and forgot it.

I'll review the diff again ... :-)

Thanks,
Gilles

On Fri, Apr 30, 2010 at 02:13:11AM -0400, Seth Wright wrote:
> Okay, I'm officially stumped.  Can someone lend me a clue as to what,
> if anything, I'm doing wrong in the following scenario?
> 
> I was playing around with smtpd(8) configuration options the other
> day, and tried to write an "accept" rule in smtpd.conf for an IPv6
> subnet.  However, when I tried to verify the config I got an error:
> 
> 
> s...@fw ~ $ grep 2001 /etc/mail/smtpd.conf
> accept from 2001:470:8:1ee::/64 for all relay
> s...@fw ~ $ smtpd -n
> smtpd: inet_net_pton: Address family not supported by protocol family
> 
> 
> I tracked down that error to /usr/src/usr.sbin/smtpd/parse.y, where in
> a few places the code is passing AF_INET6 as the address family
> argument to the function "inet_net_pton()" when it tries to parse the
> address/subnet string as an IPv6 entity (seems logical to me).
> However, in looking at /usr/src/lib/libc/net/inet_net_pton.c, that
> function does not actually accept AF_INET6 as a valid address family:
> 
> 
> 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));
>       default:
>               errno = EAFNOSUPPORT;
>               return (-1);
>       }
> }
> 
> 
> I looked around a bit and found that NetBSD does have code to handle
> the AF_INET6 case, so I borrowed their code as a test (I guess it's
> actually Paul Vixie's BIND code?), recompiled libc, and now 'smtpd -n'
> says the configuration is OK.  Great...kind of.  I did have to change
> the 'accept' rule above to add a zero at the end of the string in
> order for it to pass inspection for whatever reason:
> 
> 
> s...@bsd ~ $ grep 2001 /etc/mail/smtpd.conf
> accept from 2001:470:8:1ee::/64 for all relay
> s...@bsd ~ $ smtpd -n
> smtpd: inet_net_pton: No such file or directory
> [...]
> s...@bsd ~ $ grep 2001 /etc/mail/smtpd.conf
> accept from 2001:470:8:1ee::0/64 for all relay
> s...@bsd ~ $ smtpd -n
> configuration OK
> 
> 
> Anyway, I started smtpd up again, did a quick test, and...it still failed:
> 
> 
> s...@darwin ~ $ telnet bsd.crosse.org 25
> Trying 2001:470:8:1ee:20c:29ff:fe18:1984...
> Connected to bsd.crosse.org.
> Escape character is '^]'.
> 220 bsd.crosse.org ESMTP OpenSMTPD
> helo darwin.crosse.org
> 250 bsd.crosse.org Hello darwin.crosse.org
> [IPv6:2001:470:8:1ee:5ab0:35ff:fe78:c7a0], pleased to meet you
> mail from:<[email protected]>
> 250 2.1.0 Sender ok
> rcpt to:<[email protected]>
> 530 5.0.0 Recipient rejected: [email protected]
> quit
> 221 2.0.0 bsd.crosse.org Closing connection
> Connection closed by foreign host.
> 
> 
> The smtpd debug output for the session looks like this:
> 
> 
> s...@bsd /usr/src/usr.sbin/smtpd $ sudo smtpd -dv
> startup [debug mode]
> parent_send_config: configuring smtp
> parent_send_config_client_certs: configuring smtp
> parent_send_config_ruleset: reloading rules and maps
> parent_enqueue_offline: path /offline/1272603601.j459NrpVLg
> smtp_setup_events: listen on IPv6:2001:470:8:1ee:20c:29ff:fe18:1984
> port 25 flags 0x0 cert "vic0"
> smtp_setup_events: listen on 192.168.2.24 port 25 flags 0x0 cert "vic0"
> smtp_setup_events: listen on IPv6:fe80:1::20c:29ff:fe18:1984 port 25
> flags 0x0 cert "vic0"
> smtp_setup_events: listen on IPv6:fe80:3::1 port 25 flags 0x0 cert "lo0"
> smtp_setup_events: listen on IPv6:::1 port 25 flags 0x0 cert "lo0"
> smtp_setup_events: listen on 127.0.0.1 port 25 flags 0x0 cert "lo0"
> smtp: will accept at most 244 clients
> offline message enqueued
> session_pickup: greeting client
> command: helo   args: darwin.crosse.org
> command: mail from      args: <[email protected]>
> session_rfc5321_mail_handler: sending notification to mfa
> smtp: got imsg_mfa_mail/rcpt
> smtp: imsg_queue_create_message returned
> command: rcpt to        args: <[email protected]>
> smtp: got imsg_mfa_mail/rcpt
> 1272604479.ozbOZIoTFKlNu1MN: from=<[email protected]>,
> relay=darwin.crosse.org [IPv6:2001:470:8:1ee:5ab0:35ff:fe78:c7a0],
> stat=LocalError (530 5.0.0 Recipient rejected: [email protected])
> command: quit   args: (null)
> session_destroy: killing client: 0x86ce1000
> 
> 
> From my admittedly very limited knowledge of using gdb, I think the
> problem is in ruleset.c, in a function called
> "ruleset_inet6_match()"--however, I've stared at the code now for an
> hour or two and still have no idea what I'm looking at.  So, here's
> the big question:  did I overlook something completely obvious and
> have now gone off the deep end in searching for a bug that's not
> there?  Any hints or suggestions are welcome.  I think I can say with
> some level of confidence that this code-path will not work as-is since
> OpenBSD's inet_net_pton() only accepts AF_INET as the address family
> argument.  Adding the extra functions from NetBSD's code helped, but I
> don't know enough about this stuff to know whether the four-odd
> functions I added are "enough" or if they are really part of a much
> larger undertaking.
> 
> Thanks for your time,
> 
> Seth
> 
> (conf, dmesg, and changes to libc follow)
> 
> Note:  this was all done with -current at some point or other.  The
> dmesg below is from a VMware guest and shows a kernel from the 4/24
> snapshot, but I've been playing with this stuff on a couple of
> different machines (all either i386 or amd64).  The other one (real,
> not virtual, and currently off) was fully up-to-date with -current
> (kernel and userland) at the time of testing.  The machine below has a
> kernel and libc (with my tweaks) from 4/24, and smtpd from about an
> hour ago.
> 
> 
> s...@bsd ~ $ cat /etc/mail/smtpd.conf
> #       $OpenBSD: smtpd.conf,v 1.2 2009/11/03 22:32:10 gilles Exp $
> 
> # This is the smtpd server system-wide configuration file.
> # See smtpd.conf(5) for more information.
> 
> listen on lo0
> listen on vic0
> 
> map "aliases" { source db "/etc/mail/aliases.db" }
> 
> accept for local alias aliases deliver to mbox
> accept for all relay
> 
> accept from 2001:470:8:1ee::0/64 for all relay
> 
> ------
> 
> OpenBSD 4.7-current (GENERIC.MP) #553: Sat Apr 24 14:06:26 MDT 2010
>     [email protected]:/usr/src/sys/arch/i386/compile/GENERIC.MP
> cpu0: Intel(R) Core(TM)2 Duo CPU E4700 @ 2.60GHz ("GenuineIntel"
> 686-class) 2.60 GHz
> cpu0: 
> FPU,V86,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,SSE3,SSSE3
> real mem  = 133722112 (127MB)
> avail mem = 120479744 (114MB)
> mainbus0 at root
> bios0 at mainbus0: AT/286+ BIOS, date 09/22/09, BIOS32 rev. 0 @
> 0xfd780, SMBIOS rev. 2.4 @ 0xe0010 (98 entries)
> bios0: vendor Phoenix Technologies LTD version "6.00" date 09/22/2009
> bios0: VMware, Inc. VMware Virtual Platform
> acpi0 at bios0: rev 2
> acpi0: tables DSDT FACP BOOT APIC MCFG SRAT
> acpi0: wakeup devices PCI0(S3) USB_(S1) P2P0(S3) S1F0(S3) S2F0(S3)
> S3F0(S3) S4F0(S3) S5F0(S3) S6F0(S3) S7F0(S3) S8F0(S3) S9F0(S3)
> Z00P(S3) Z00Q(S3) Z00R(S3) Z00S(S3) Z00T(S3) Z00U(S3) Z00V(S3)
> Z00W(S3) Z00X(S3) Z00Y(S3) Z00Z(S3) Z010(S3) Z011(S3) Z012(S3)
> Z013(S3) Z014(S3) Z015(S3) Z016(S3) Z017(S3) Z018(S3) Z019(S3)
> Z01A(S3) Z01B(S3) P2P1(S3) S1F0(S3) S2F0(S3) S3F0(S3) S4F0(S3)
> S5F0(S3) S6F0(S3) S7F0(S3) S8F0(S3) S9F0(S3) Z00P(S3) Z00Q(S3)
> Z00R(S3) Z00S(S3) Z00T(S3) Z00U(S3) Z00V(S3) Z00W(S3) Z00X(S3)
> Z00Y(S3) Z00Z(S3) Z010(S3) Z011(S3) Z012(S3) Z013(S3) Z014(S3)
> Z015(S3) Z016(S3) Z017(S3) Z018(S3) Z019(S3) Z01A(S3) Z01B(S3)
> P2P2(S3) S1F0(S3) S2F0(S3) S3F0(S3) S4F0(S3) S5F0(S3) S6F0(S3)
> S7F0(S3) S8F0(S3) S9F0(S3) Z00P(S3) Z00Q(S3) Z00R(S3) Z00S(S3)
> Z00T(S3) Z00U(S3) Z00V(S3) Z00W(S3) Z00X(S3) Z00Y(S3) Z00Z(S3)
> Z010(S3) Z011(S3) Z012(S3) Z013(S3) Z014(S3) Z015(S3) Z016(S3)
> Z017(S3) Z018(S3) Z019(S3) Z01A(S3) Z01B(S3) P2P3(S3) S1F0(S3)
> S2F0(S3) S3F0(S3) S4F0(S3) S5F0(S3) S6F0(S3) S7F0(S3) S8F0(S3)
> S9F0(S3) Z00P(S3) Z00Q(S3) Z00R(S3) Z00S(S3) Z00T(S3) Z00U(S3)
> Z00V(S3) Z00W(S3) Z00X(S3) Z00Y(S3) Z00Z(S3) Z010(S3) Z011(S3)
> Z012(S3) Z013(S3) Z014(S3) Z015(S3) Z016(S3) Z017(S3) Z018(S3)
> Z019(S3) Z01A(S3) Z01B(S3) PE40(S3) S1F0(S3) PE50(S3) S1F0(S3)
> PE60(S3) S1F0(S3) PE70(S3) S1F0(S3) PE80(S3) S1F0(S3) PE90(S3)
> S1F0(S3) PEA0(S3) S1F0(S3) PEB0(S3) S1F0(S3) PEC0(S3) S1F0(S3)
> PED0(S3) S1F0(S3) PEE0(S3) S1F0(S3) PE41(S3) S1F0(S3) PE42(S3)
> S1F0(S3) PE43(S3) S1F0(S3) PE44(S3) S1F0(S3) PE45(S3) S1F0(S3)
> PE46(S3) S1F0(S3) PE47(S3) S1F0(S3) PE51(S3) S1F0(S3) PE52(S3)
> S1F0(S3) PE53(S3) S1F0(S3) PE54(S3) S1F0(S3) PE55(S3) S1F0(S3)
> PE56(S3) S1F0(S3) PE57(S3) S1F0(S3) PE61(S3) S1F0(S3) PE62(S3)
> S1F0(S3) PE63(S3) S1F0(S3) PE64(S3) S1F0(S3) PE65(S3) S1F0(S3)
> PE66(S3) S1F0(S3) PE67(S3) S1F0(S3) PE71(S3) S1F0(S3) PE72(S3)
> S1F0(S3) PE73(S3) S1F0(S3) PE74(S3) S1F0(S3) PE75(S3) S1F0(S3)
> PE76(S3) S1F0(S3) PE77(S3) S1F0(S3) PE81(S3) S1F0(S3) PE82(S3)
> S1F0(S3) PE83(S3) S1F0(S3) PE84(S3) S1F0(S3) PE85(S3) S1F0(S3)
> PE86(S3) S1F0(S3) PE87(S3) S1F0(S3) PE91(S3) S1F0(S3) PE92(S3)
> S1F0(S3) PE93(S3) S1F0(S3) PE94(S3) S1F0(S3) PE95(S3) S1F0(S3)
> PE96(S3) S1F0(S3) PE97(S3) S1F0(S3) PEA1(S3) S1F0(S3) PEA2(S3)
> S1F0(S3) PEA3(S3) S1F0(S3) PEA4(S3) S1F0(S3) PEA5(S3) S1F0(S3)
> PEA6(S3) S1F0(S3) PEA7(S3) S1F0(S3) PEB1(S3) S1F0(S3) PEB2(S3)
> S1F0(S3) PEB3(S3) S1F0(S3) PEB4(S3) S1F0(S3) PEB5(S3) S1F0(S3)
> PEB6(S3) S1F0(S3) PEB7(S3) S1F0(S3) SLPB(S4)
> acpitimer0 at acpi0: 3579545 Hz, 24 bits
> acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
> cpu0 at mainbus0: apid 0 (boot processor)
> cpu0: apic clock running at 65MHz
> cpu1 at mainbus0: apid 1 (application processor)
> cpu1: Intel(R) Core(TM)2 Duo CPU E4700 @ 2.60GHz ("GenuineIntel"
> 686-class) 2.60 GHz
> cpu1: 
> FPU,V86,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,SSE3,SSSE3
> ioapic0 at mainbus0: apid 2 pa 0xfec00000, version 11, 24 pins
> acpiprt0 at acpi0: bus 0 (PCI0)
> acpicpu0 at acpi0
> acpicpu1 at acpi0
> acpibat0 at acpi0: BAT1 not present
> acpibat1 at acpi0: BAT2 not present
> acpiac0 at acpi0: AC unit online
> acpibtn0 at acpi0: SLPB
> bios0: ROM list: 0xc0000/0x8000 0xc8000/0x1e00! 0xca000/0x1000
> 0xdc000/0x4000! 0xe0000/0x4000!
> pci0 at mainbus0 bus 0: configuration mode 1 (bios)
> pchb0 at pci0 dev 0 function 0 "Intel 82443BX AGP" rev 0x01
> ppb0 at pci0 dev 1 function 0 "Intel 82443BX AGP" rev 0x01
> pci1 at ppb0 bus 1
> piixpcib0 at pci0 dev 7 function 0 "Intel 82371AB PIIX4 ISA" rev 0x08
> pciide0 at pci0 dev 7 function 1 "Intel 82371AB IDE" rev 0x01: DMA,
> channel 0 configured to compatibility, channel 1 configured to
> compatibility
> pciide0: channel 0 ignored (disabled)
> atapiscsi0 at pciide0 channel 1 drive 0
> scsibus0 at atapiscsi0: 2 targets
> cd0 at scsibus0 targ 0 lun 0: <NECVMWar, VMware IDE CDR10, 1.00> ATAPI
> 5/cdrom removable
> cd0(pciide0:1:0): using PIO mode 4, Ultra-DMA mode 2
> piixpm0 at pci0 dev 7 function 3 "Intel 82371AB Power" rev 0x08: SMBus 
> disabled
> "VMware Virtual Machine Communication Interface" rev 0x10 at pci0 dev
> 7 function 7 not configured
> vga1 at pci0 dev 15 function 0 "VMware Virtual SVGA II" rev 0x00
> wsdisplay0 at vga1 mux 1: console (80x25, vt100 emulation)
> wsdisplay0: screen 1-5 added (80x25, vt100 emulation)
> mpi0 at pci0 dev 16 function 0 "Symbios Logic 53c1030" rev 0x01: apic
> 2 int 17 (irq 11)
> scsibus1 at mpi0: 16 targets, initiator 7
> sd0 at scsibus1 targ 0 lun 0: <VMware, Virtual disk, 1.0> SCSI2 0/direct fixed
> sd0: 51200MB, 512 bytes/sec, 104857600 sec total
> sd1 at scsibus1 targ 1 lun 0: <VMware, Virtual disk, 1.0> SCSI2 0/direct fixed
> sd1: 102400MB, 512 bytes/sec, 209715200 sec total
> sd2 at scsibus1 targ 2 lun 0: <VMware, Virtual disk, 1.0> SCSI2 0/direct fixed
> sd2: 102400MB, 512 bytes/sec, 209715200 sec total
> mpi0: target 0 Sync at 160MHz width 16bit offset 127 QAS 1 DT 1 IU 1
> mpi0: target 1 Sync at 160MHz width 16bit offset 127 QAS 1 DT 1 IU 1
> mpi0: target 2 Sync at 160MHz width 16bit offset 127 QAS 1 DT 1 IU 1
> ppb1 at pci0 dev 17 function 0 "VMware Virtual PCI-PCI" rev 0x02
> pci2 at ppb1 bus 2
> vic0 at pci2 dev 0 function 0 "AMD 79c970 PCnet-PCI" rev 0x10: apic 2
> int 18 (irq 10), address 00:0c:29:18:19:84
> ppb2 at pci0 dev 21 function 0 "VMware Virtual PCIE-PCIE" rev 0x01
> pci3 at ppb2 bus 3
> ppb3 at pci0 dev 21 function 1 "VMware Virtual PCIE-PCIE" rev 0x01
> pci4 at ppb3 bus 4
> ppb4 at pci0 dev 21 function 2 "VMware Virtual PCIE-PCIE" rev 0x01
> pci5 at ppb4 bus 5
> ppb5 at pci0 dev 21 function 3 "VMware Virtual PCIE-PCIE" rev 0x01
> pci6 at ppb5 bus 6
> ppb6 at pci0 dev 21 function 4 "VMware Virtual PCIE-PCIE" rev 0x01
> pci7 at ppb6 bus 7
> ppb7 at pci0 dev 21 function 5 "VMware Virtual PCIE-PCIE" rev 0x01
> pci8 at ppb7 bus 8
> ppb8 at pci0 dev 21 function 6 "VMware Virtual PCIE-PCIE" rev 0x01
> pci9 at ppb8 bus 9
> ppb9 at pci0 dev 21 function 7 "VMware Virtual PCIE-PCIE" rev 0x01
> pci10 at ppb9 bus 10
> ppb10 at pci0 dev 22 function 0 "VMware Virtual PCIE-PCIE" rev 0x01
> pci11 at ppb10 bus 11
> ppb11 at pci0 dev 22 function 1 "VMware Virtual PCIE-PCIE" rev 0x01
> pci12 at ppb11 bus 12
> ppb12 at pci0 dev 22 function 2 "VMware Virtual PCIE-PCIE" rev 0x01
> pci13 at ppb12 bus 13
> ppb13 at pci0 dev 22 function 3 "VMware Virtual PCIE-PCIE" rev 0x01
> pci14 at ppb13 bus 14
> ppb14 at pci0 dev 22 function 4 "VMware Virtual PCIE-PCIE" rev 0x01
> pci15 at ppb14 bus 15
> ppb15 at pci0 dev 22 function 5 "VMware Virtual PCIE-PCIE" rev 0x01
> pci16 at ppb15 bus 16
> ppb16 at pci0 dev 22 function 6 "VMware Virtual PCIE-PCIE" rev 0x01
> pci17 at ppb16 bus 17
> ppb17 at pci0 dev 22 function 7 "VMware Virtual PCIE-PCIE" rev 0x01
> pci18 at ppb17 bus 18
> ppb18 at pci0 dev 23 function 0 "VMware Virtual PCIE-PCIE" rev 0x01
> pci19 at ppb18 bus 19
> ppb19 at pci0 dev 23 function 1 "VMware Virtual PCIE-PCIE" rev 0x01
> pci20 at ppb19 bus 20
> ppb20 at pci0 dev 23 function 2 "VMware Virtual PCIE-PCIE" rev 0x01
> pci21 at ppb20 bus 21
> ppb21 at pci0 dev 23 function 3 "VMware Virtual PCIE-PCIE" rev 0x01
> pci22 at ppb21 bus 22
> ppb22 at pci0 dev 23 function 4 "VMware Virtual PCIE-PCIE" rev 0x01
> pci23 at ppb22 bus 23
> ppb23 at pci0 dev 23 function 5 "VMware Virtual PCIE-PCIE" rev 0x01
> pci24 at ppb23 bus 24
> ppb24 at pci0 dev 23 function 6 "VMware Virtual PCIE-PCIE" rev 0x01
> pci25 at ppb24 bus 25
> ppb25 at pci0 dev 23 function 7 "VMware Virtual PCIE-PCIE" rev 0x01
> pci26 at ppb25 bus 26
> ppb26 at pci0 dev 24 function 0 "VMware Virtual PCIE-PCIE" rev 0x01
> pci27 at ppb26 bus 27
> ppb27 at pci0 dev 24 function 1 "VMware Virtual PCIE-PCIE" rev 0x01
> pci28 at ppb27 bus 28
> ppb28 at pci0 dev 24 function 2 "VMware Virtual PCIE-PCIE" rev 0x01
> pci29 at ppb28 bus 29
> ppb29 at pci0 dev 24 function 3 "VMware Virtual PCIE-PCIE" rev 0x01
> pci30 at ppb29 bus 30
> ppb30 at pci0 dev 24 function 4 "VMware Virtual PCIE-PCIE" rev 0x01
> pci31 at ppb30 bus 31
> ppb31 at pci0 dev 24 function 5 "VMware Virtual PCIE-PCIE" rev 0x01
> pci32 at ppb31 bus 32
> ppb32 at pci0 dev 24 function 6 "VMware Virtual PCIE-PCIE" rev 0x01
> pci33 at ppb32 bus 33
> ppb33 at pci0 dev 24 function 7 "VMware Virtual PCIE-PCIE" rev 0x01
> pci34 at ppb33 bus 34
> isa0 at piixpcib0
> isadma0 at isa0
> com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo
> com1 at isa0 port 0x2f8/8 irq 3: ns16550a, 16 byte fifo
> pckbc0 at isa0 port 0x60/5
> pckbd0 at pckbc0 (kbd slot)
> pckbc0: using irq 1 for kbd slot
> wskbd0 at pckbd0: console keyboard, using wsdisplay0
> pmsi0 at pckbc0 (aux slot)
> pckbc0: using irq 12 for aux slot
> wsmouse0 at pmsi0 mux 0
> pcppi0 at isa0 port 0x61
> midi0 at pcppi0: <PC speaker>
> spkr0 at pcppi0
> lpt0 at isa0 port 0x378/4 irq 7
> npx0 at isa0 port 0xf0/16: reported by CPUID; using exception 16
> fdc0 at isa0 port 0x3f0/6 irq 6 drq 2
> fd0 at fdc0 drive 0: 1.44MB 80 cyl, 2 head, 18 sec
> mtrr: Pentium Pro MTRR support
> vscsi0 at root
> scsibus2 at vscsi0: 256 targets
> softraid0 at root
> root on sd0a swap on sd0b dump on sd0b
> 
> ------------------
> 
> Finally, here's the diff of what I pulled from NetBSD and applied to libc:
> 
> 
> Index: inet_net_ntop.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/net/inet_net_ntop.c,v
> retrieving revision 1.6
> diff -u -p inet_net_ntop.c
> --- inet_net_ntop.c   6 Aug 2005 20:30:03 -0000       1.6
> +++ inet_net_ntop.c   30 Apr 2010 05:52:37 -0000
> @@ -27,7 +27,8 @@
>  #include <string.h>
>  #include <stdlib.h>
> 
> -static char *inet_net_ntop_ipv4(const u_char *, int, char *, size_t);
> +static char *        inet_net_ntop_ipv4(const u_char *, int, char *, size_t);
> +static char *        inet_net_ntop_ipv6(const u_char *, int, char *, size_t);
> 
>  /*
>   * char *
> @@ -45,6 +46,8 @@ inet_net_ntop(int af, const void *src, int bits, char
>       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);
> @@ -130,6 +133,152 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char *
>       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)
> +{
> +     size_t  bytes;
> +     u_int   m;
> +     int     b;
> +     int     p;
> +     int     zero_s, zero_l, tmp_zero_s, tmp_zero_l;
> +     int     i;
> +     int     is_ipv4 = 0;
> +     u_char inbuf[16];
> +     char 
> outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
> +     char    *cp;
> +     int     words;
> +     u_char  *s;
> +     char    *ep;
> +     int     advance;
> +
> +     if (bits < 0 || bits > 128) {
> +             errno = EINVAL;
> +             return (NULL);
> +     }
> +
> +     cp = outbuf;
> +     ep = outbuf + sizeof(outbuf);
> +
> +     if (bits == 0) {
> +             *cp++ = ':';
> +             *cp++ = ':';
> +             *cp = '\0';
> +     } else {
> +             /* Copy src to private buffer.  Zero host part. */      
> +             bytes = (bits + 7) / 8;
> +             memcpy(inbuf, src, bytes);
> +             memset(inbuf + bytes, 0, 16 - bytes);
> +             b = bits % 8;
> +             if (b != 0) {
> +                     m = ~0 << (8 - b);
> +                     inbuf[bytes-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) ? ':' : '.';
> +                             advance = snprintf(cp, (size_t)(ep - cp),
> +                                 "%u", *s++);
> +                             if (advance <= 0 || advance >= ep - cp)
> +                                     goto emsgsize;
> +                             cp += advance;
> +                             /* we can potentially drop the last octet */
> +                             if (p != 7 || bits > 120) {
> +                                     *cp++ = '.';
> +                                     advance = snprintf(cp,
> +                                         (size_t)(ep - cp), "%u", *s++);
> +                                     if (advance <= 0 || advance >= ep - cp)
> +                                             goto emsgsize;
> +                                     cp += advance;
> +                             }
> +                     } else {
> +                             if (cp != outbuf)
> +                                     *cp++ = ':';
> +                             advance = snprintf(cp, (size_t)(ep - cp), "%x",
> +                                 *s * 256 + s[1]);
> +                             if (advance <= 0 || advance >= ep - cp)
> +                                     goto emsgsize;
> +                             cp += advance;
> +                             s += 2;
> +                     }
> +             }
> +     }
> +     /* Format CIDR /width. */
> +     /* LINTED */
> +     snprintf(cp, ep - cp, "/%u", bits);
> +     if (strlen(outbuf) + 1 > size)
> +             goto emsgsize;
> +     strlcpy(dst, outbuf, size);
> +     
> +     return (dst);
> +
> +emsgsize:
>       errno = EMSGSIZE;
>       return (NULL);
>  }
> Index: inet_net_pton.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/net/inet_net_pton.c,v
> retrieving revision 1.6
> diff -u -p inet_net_pton.c
> --- inet_net_pton.c   1 Sep 2008 09:40:43 -0000       1.6
> +++ inet_net_pton.c   30 Apr 2010 05:52:37 -0000
> @@ -21,6 +21,7 @@
>  #include <sys/socket.h>
>  #include <netinet/in.h>
>  #include <arpa/inet.h>
> +#include <arpa/nameser.h>
> 
>  #include <assert.h>
>  #include <ctype.h>
> @@ -30,6 +31,9 @@
>  #include <stdlib.h>
> 
>  static int   inet_net_pton_ipv4(const char *, u_char *, size_t);
> +static int   inet_net_pton_ipv6(const char *, u_char *, size_t);
> +static int   getbits(const char *, int *);
> +static int   getv4(const char *, u_char *, int *);
> 
>  /*
>   * static int
> @@ -50,6 +54,8 @@ inet_net_pton(int af, const char *src, void *dst, 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);
> @@ -179,6 +185,201 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_
>                       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[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', IN6ADDRSZ);
> +     endp = tp + 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 + 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 + INADDRSZ) <= endp) &&
> +                  getv4(curtok, tp, &bits) > 0) {
> +                     tp += 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 + 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:
> 

-- 
Gilles Chehade
freelance developer/sysadmin/consultant

                   http://www.poolp.org

Reply via email to