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: