@@ -59,7 +59,7 @@ func newRawSpoofer(conn net.Conn, method Method) (Spoofer, error) {
5959 if err != nil {
6060 return nil , err
6161 }
62- fd , sockaddr , err := openDarwinRawSocket (dst )
62+ fd , sockaddr , err := openDarwinRawSocket (src , dst )
6363 if err != nil {
6464 return nil , err
6565 }
@@ -119,31 +119,51 @@ func readDarwinTCPSequence(src, dst netip.AddrPort) (uint32, uint32, error) {
119119 return 0 , 0 , E .New ("tls_spoof: connection " , src , "->" , dst , " not found in pcblist_n" )
120120}
121121
122- func openDarwinRawSocket (dst netip.AddrPort ) (int , unix.Sockaddr , error ) {
123- if ! dst .Addr ().Is4 () {
124- // macOS does not expose IPV6_HDRINCL; raw AF_INET6 injection would
125- // require either BPF link-layer writes or kernel-side IPv6 header
126- // synthesis, neither of which is implemented here.
127- return - 1 , nil , E .New ("tls_spoof: IPv6 not supported on darwin" )
122+ func openDarwinRawSocket (src , dst netip.AddrPort ) (int , unix.Sockaddr , error ) {
123+ if dst .Addr ().Is4 () {
124+ return openIPv4RawSocket (dst )
128125 }
129- return openIPv4RawSocket (dst )
126+ // macOS does not accept IPV6_HDRINCL on AF_INET6 SOCK_RAW IPPROTO_TCP
127+ // sockets, so the kernel builds the IPv6 header itself. Bind to the real
128+ // connection's source address so in6_selectsrc returns it, and rely on
129+ // in6p_cksum defaulting to -1 so the user-supplied TCP checksum is
130+ // preserved (including deliberately corrupted ones).
131+ fd , err := unix .Socket (unix .AF_INET6 , unix .SOCK_RAW , unix .IPPROTO_TCP )
132+ if err != nil {
133+ return - 1 , nil , E .Cause (err , "open AF_INET6 SOCK_RAW" )
134+ }
135+ err = unix .Bind (fd , & unix.SockaddrInet6 {Addr : src .Addr ().As16 ()})
136+ if err != nil {
137+ unix .Close (fd )
138+ return - 1 , nil , E .Cause (err , "bind AF_INET6 SOCK_RAW" )
139+ }
140+ sockaddr := & unix.SockaddrInet6 {Port : int (dst .Port ()), Addr : dst .Addr ().As16 ()}
141+ return fd , sockaddr , nil
130142}
131143
132144func (s * darwinSpoofer ) Inject (payload []byte ) error {
145+ if ! s .src .Addr ().Is4 () {
146+ segment , err := buildSpoofTCPSegment (s .method , s .src , s .dst , s .sendNext , s .receiveNext , payload )
147+ if err != nil {
148+ return err
149+ }
150+ err = unix .Sendto (s .rawFD , segment , 0 , s .rawSockAddr )
151+ if err != nil {
152+ return E .Cause (err , "sendto raw socket" )
153+ }
154+ return nil
155+ }
133156 frame , err := buildSpoofFrame (s .method , s .src , s .dst , s .sendNext , s .receiveNext , payload )
134157 if err != nil {
135158 return err
136159 }
137160 // Darwin inherits the historical BSD quirk: with IP_HDRINCL the kernel
138161 // expects ip_len and ip_off in host byte order, not network byte order.
139- // Apple's rip_output swaps them back before transmission. This does not
140- // apply to IPv6.
141- if s .src .Addr ().Is4 () {
142- totalLen := binary .BigEndian .Uint16 (frame [2 :4 ])
143- binary .NativeEndian .PutUint16 (frame [2 :4 ], totalLen )
144- fragOff := binary .BigEndian .Uint16 (frame [6 :8 ])
145- binary .NativeEndian .PutUint16 (frame [6 :8 ], fragOff )
146- }
162+ // Apple's rip_output swaps them back before transmission.
163+ totalLen := binary .BigEndian .Uint16 (frame [2 :4 ])
164+ binary .NativeEndian .PutUint16 (frame [2 :4 ], totalLen )
165+ fragOff := binary .BigEndian .Uint16 (frame [6 :8 ])
166+ binary .NativeEndian .PutUint16 (frame [6 :8 ], fragOff )
147167 err = unix .Sendto (s .rawFD , frame , 0 , s .rawSockAddr )
148168 if err != nil {
149169 return E .Cause (err , "sendto raw socket" )
0 commit comments