capturing TCP packets working
This commit is contained in:
parent
9aa7d66331
commit
7341f61e48
|
@ -0,0 +1,46 @@
|
||||||
|
from nativesockets import getAddrString
|
||||||
|
from posix import SockAddr, Sockaddr_in, inet_ntoa, AF_INET
|
||||||
|
|
||||||
|
type
|
||||||
|
NetworkInterface* = object
|
||||||
|
ipAddress*: string
|
||||||
|
name*: string
|
||||||
|
index*: cint
|
||||||
|
flags*: cuint
|
||||||
|
|
||||||
|
NetworkInterfaceError* = object of ValueError
|
||||||
|
|
||||||
|
Ifaddrs {.importc: "struct ifaddrs", pure, final,
|
||||||
|
header: "<ifaddrs.h>".} = object
|
||||||
|
ifa_next: ptr Ifaddrs # Next item in list
|
||||||
|
ifa_name: cstring # Name of interface
|
||||||
|
ifa_flags: cuint # Flags from SIOCGIFFLAGS
|
||||||
|
ifa_addr: ptr SockAddr # Address of interface
|
||||||
|
ifa_netmask: ptr SockAddr # Netmask of interface
|
||||||
|
ifa_ifu: ptr SockAddr # Broadcast / point-to-point address (we don't care about the union)
|
||||||
|
ifa_data: pointer # Address-specific data
|
||||||
|
|
||||||
|
proc getifaddrs(ifap: ptr ptr Ifaddrs): int {.header: "<ifaddrs.h>", importc: "getifaddrs".}
|
||||||
|
proc freeifaddrs(ifap: ptr Ifaddrs): void {.header: "<ifaddrs.h>", importc: "freeifaddrs".}
|
||||||
|
proc if_nametoindex(ifname: cstring): cuint {.header: "<net/if.h>", importc: "if_nametoindex".}
|
||||||
|
|
||||||
|
proc fromIpAddress*(address: string): NetworkInterface =
|
||||||
|
var interfaces: ptr Ifaddrs
|
||||||
|
if getifaddrs(addr interfaces) != 0:
|
||||||
|
raise newException(NetworkInterfaceError, "getifaddrs failed")
|
||||||
|
var it = interfaces
|
||||||
|
while it != nil:
|
||||||
|
if it.ifa_addr != nil and
|
||||||
|
it.ifa_addr.sa_family.cint == AF_INET and
|
||||||
|
it.ifa_addr.getAddrString() == address:
|
||||||
|
result.ipAddress = address
|
||||||
|
result.name = $it.ifa_name
|
||||||
|
result.index = if_nametoindex(result.name).cint
|
||||||
|
result.flags = it.ifa_flags
|
||||||
|
break
|
||||||
|
it = it.ifa_next
|
||||||
|
freeifaddrs(interfaces)
|
||||||
|
if result.name == "":
|
||||||
|
raise newException(NetworkInterfaceError, "interface for given IP address not found")
|
||||||
|
if result.index <= 0:
|
||||||
|
raise newException(NetworkInterfaceError, "cannot get interface index")
|
|
@ -0,0 +1,82 @@
|
||||||
|
from nativesockets import Port, ntohs, ntohl
|
||||||
|
from posix import InAddr, inet_ntoa
|
||||||
|
|
||||||
|
type
|
||||||
|
Ether_header {.importc: "struct ether_header", pure, final,
|
||||||
|
header: "<netinet/if_ether.h>".} = object
|
||||||
|
ether_dhost: array[6, cuchar]
|
||||||
|
ether_shost: array[6, cuchar]
|
||||||
|
ether_type: cushort
|
||||||
|
|
||||||
|
Ip {.importc: "struct ip", pure, final,
|
||||||
|
header: "<netinet/ip.h>".} = object
|
||||||
|
when cpuEndian == littleEndian:
|
||||||
|
ip_hl {.bitsize:4.}: cuchar # header length
|
||||||
|
ip_v {.bitsize:4.}: cuchar # version
|
||||||
|
else:
|
||||||
|
ip_v {.bitsize:4.}: cuchar # version
|
||||||
|
ip_hl {.bitsize:4.}: cuchar # header length
|
||||||
|
ip_tos: cuchar # type of service
|
||||||
|
ip_len: cshort # total length
|
||||||
|
ip_id: cushort # identification
|
||||||
|
ip_off: cshort # fragment offset field
|
||||||
|
ip_ttl: cuchar # time to live
|
||||||
|
ip_p: cuchar # protocol
|
||||||
|
ip_sum: cushort # checksum
|
||||||
|
ip_src: InAddr # source address
|
||||||
|
ip_dst: InAddr # destination address
|
||||||
|
|
||||||
|
Tcphdr {.importc: "struct tcphdr", pure, final,
|
||||||
|
header: "<netinet/tcp.h>".} = object
|
||||||
|
th_sport: cushort # source port
|
||||||
|
th_dport: cushort # destination port
|
||||||
|
th_seq: uint32 # sequence number
|
||||||
|
th_ack: uint32 # ackkowledgment number
|
||||||
|
when cpuEndian == littleEndian:
|
||||||
|
th_x2 {.bitsize:4.}: cuchar # (unused)
|
||||||
|
th_off {.bitsize:4.}: cuchar # data offset
|
||||||
|
else:
|
||||||
|
th_off {.bitsize:4.}: cuchar # (unused)
|
||||||
|
th_x2 {.bitsize:4.}: cuchar # data offset
|
||||||
|
th_flags: cuchar # flags
|
||||||
|
th_win: cushort # window
|
||||||
|
th_sum: cushort # checksum
|
||||||
|
th_urp: cushort # urgent pointer
|
||||||
|
|
||||||
|
Protocol* = enum
|
||||||
|
tcp
|
||||||
|
other
|
||||||
|
|
||||||
|
PacketInfo* = object
|
||||||
|
case protocol*: Protocol
|
||||||
|
of tcp:
|
||||||
|
tcpIpSrc*: string
|
||||||
|
tcpIpDst*: string
|
||||||
|
tcpPortSrc*: Port
|
||||||
|
tcpPortDst*: Port
|
||||||
|
tcpSeqNumber*: uint32
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
var
|
||||||
|
ETHERTYPE_IP {.importc: "ETHERTYPE_IP", header: "<netinet/if_ether.h>".}: cushort
|
||||||
|
IPPROTO_TCP {.importc: "IPPROTO_TCP", header: "<netinet/in.h>".}: cint
|
||||||
|
|
||||||
|
proc fromString*(packet: string): PacketInfo =
|
||||||
|
let etherHeader = cast[ptr Ether_header](packet.cstring)
|
||||||
|
if ntohs(etherHeader.ether_type) == ETHERTYPE_IP:
|
||||||
|
let ipHeader = cast[ptr Ip](cast[int](packet.cstring) + sizeof(Ether_header))
|
||||||
|
let ipSrc = $inet_ntoa(ipHeader.ip_src)
|
||||||
|
let ipDst = $inet_ntoa(ipHeader.ip_dst)
|
||||||
|
if ipHeader.ip_p.int == IPPROTO_TCP:
|
||||||
|
let tcpHeader = cast[ptr Tcphdr](cast[int](ipHeader) + ipHeader.ip_hl.int * 4)
|
||||||
|
result = PacketInfo(protocol: tcp,
|
||||||
|
tcpIpSrc: ipSrc,
|
||||||
|
tcpIpDst: ipDst,
|
||||||
|
tcpPortSrc: Port(ntohs(tcpHeader.th_sport)),
|
||||||
|
tcpPortDst: Port(ntohs(tcpHeader.th_dport)),
|
||||||
|
tcpSeqNumber: ntohl(tcpHeader.th_seq))
|
||||||
|
else:
|
||||||
|
result = PacketInfo(protocol: other)
|
||||||
|
else:
|
||||||
|
result = PacketInfo(protocol: other)
|
|
@ -0,0 +1,85 @@
|
||||||
|
import
|
||||||
|
asyncdispatch,
|
||||||
|
network_interface,
|
||||||
|
packet_info
|
||||||
|
|
||||||
|
from nativesockets import SOCK_RAW, bindAddr, htons
|
||||||
|
from posix import ioctl, setsockopt, SockAddr, SockLen, SocketHandle
|
||||||
|
|
||||||
|
export NetworkInterfaceError
|
||||||
|
|
||||||
|
type
|
||||||
|
Sockaddr_ll {.importc: "struct sockaddr_ll", pure, final,
|
||||||
|
header: "<linux/if_packet.h>".} = object
|
||||||
|
sll_family: cushort # Always AF_PACKET
|
||||||
|
sll_protocol: cushort # Physical-layer protocol
|
||||||
|
sll_ifindex: cint # Interface number
|
||||||
|
sll_hatype: cushort # ARP hardware type
|
||||||
|
sll_pkttype: cuchar # Packet type
|
||||||
|
sll_halen: cuchar # Length of address
|
||||||
|
sll_addr: array[8, cuchar] # Physical-layer address
|
||||||
|
|
||||||
|
Packet_mreq {.importc: "struct packet_mreq", pure, final,
|
||||||
|
header: "<linux/if_packet.h>".} = object
|
||||||
|
mr_ifindex: cint
|
||||||
|
mr_type: cushort
|
||||||
|
mr_alen: cushort
|
||||||
|
mr_address: array[8, cuchar]
|
||||||
|
|
||||||
|
PunchHoleError* = object of ValueError
|
||||||
|
|
||||||
|
var
|
||||||
|
AF_PACKET {.importc: "AF_PACKET", header: "<sys/socket.h>".}: cushort
|
||||||
|
SOL_PACKET {.importc: "SOL_PACKET", header: "<sys/socket.h>".}: cushort
|
||||||
|
ETH_P_ALL {.importc: "ETH_P_ALL", header: "<linux/if_ether.h>".}: cushort
|
||||||
|
PACKET_ADD_MEMBERSHIP {.importc: "PACKET_ADD_MEMBERSHIP", header: "<linux/if_packet.h>".}: cushort
|
||||||
|
PACKET_MR_PROMISC {.importc: "PACKET_MR_PROMISC", header: "<linux/if_packet.h>".}: cushort
|
||||||
|
|
||||||
|
proc punchHoleAsClient*(clientAddress: string,
|
||||||
|
clientPort: Port,
|
||||||
|
serverAddress: string,
|
||||||
|
serverPort: Port) {.async.} =
|
||||||
|
echo "punchHoleAsClient"
|
||||||
|
# Create a raw packet socket. For accessing outgoing packets we need to use
|
||||||
|
# ETH_P_ALL which is needed in network byte order, see packet(7) man page.
|
||||||
|
let rawFd = createAsyncNativeSocket(AF_PACKET.cint,
|
||||||
|
SOCK_RAW.cint,
|
||||||
|
htons(ETH_P_ALL).cint)
|
||||||
|
|
||||||
|
# Limit capturing of packets to the desired network interface, see
|
||||||
|
# netdevice(7) man page
|
||||||
|
let iface = fromIpAddress(clientAddress)
|
||||||
|
echo "interface: ", iface.name, ", index: ", iface.index
|
||||||
|
var sa = Sockaddr_ll(sll_family: AF_PACKET,
|
||||||
|
sll_protocol: htons(ETH_P_ALL),
|
||||||
|
sll_ifindex: iface.index)
|
||||||
|
if bindAddr(rawFd.SocketHandle,
|
||||||
|
cast[ptr SockAddr](addr sa),
|
||||||
|
sizeof(Sockaddr_ll).SockLen) != 0:
|
||||||
|
raise newException(PunchHoleError, "cannot bind to interface")
|
||||||
|
|
||||||
|
# Enable promiscuous mode, see netdevice(7) man page
|
||||||
|
var req = Packet_mreq(mr_ifindex: iface.index, mr_type: PACKET_MR_PROMISC)
|
||||||
|
if setsockopt(rawFd.SocketHandle,
|
||||||
|
SOL_PACKET.cint,
|
||||||
|
PACKET_ADD_MEMBERSHIP.cint,
|
||||||
|
addr req,
|
||||||
|
sizeof(req).SockLen) != 0:
|
||||||
|
raise newException(PunchHoleError, "cannot enable promiscuous mode")
|
||||||
|
|
||||||
|
while true:
|
||||||
|
let packet = await rawFd.recv(4000)
|
||||||
|
if packet == "":
|
||||||
|
break
|
||||||
|
echo "packet len: ", packet.len
|
||||||
|
let packetInfo = fromString(packet)
|
||||||
|
if packetInfo.protocol == tcp and
|
||||||
|
packetInfo.tcpIpSrc == clientAddress and
|
||||||
|
packetInfo.tcpPortSrc.int == clientPort.int and
|
||||||
|
packetInfo.tcpIpDst == serverAddress and
|
||||||
|
packetInfo.tcpPortDst.int == serverPort.int:
|
||||||
|
echo "captured packet, sequence number: ", packetInfo.tcpSeqNumber
|
||||||
|
break
|
||||||
|
|
||||||
|
closeSocket(rawFd)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import
|
import
|
||||||
|
net,
|
||||||
asyncnet,
|
asyncnet,
|
||||||
asyncdispatch,
|
asyncdispatch,
|
||||||
asyncreadline
|
asyncreadline,
|
||||||
|
punch
|
||||||
|
|
||||||
from strutils import format
|
from strutils import format, join
|
||||||
from nativesockets import setSockOptInt
|
from nativesockets import setSockOptInt
|
||||||
from os import paramCount, paramStr
|
from os import paramCount, paramStr
|
||||||
|
|
||||||
|
@ -13,21 +15,22 @@ var IP_TTL {.importc: "IP_TTL", header: "<netinet/in.h>".}: cint
|
||||||
proc usage() =
|
proc usage() =
|
||||||
echo("Usage: $# client|server".format(paramStr(0)))
|
echo("Usage: $# client|server".format(paramStr(0)))
|
||||||
|
|
||||||
#proc punchHoleAsClient(socket: AsyncSocket) {.async.} =
|
|
||||||
#
|
|
||||||
|
|
||||||
proc processInput(client: AsyncSocket) {.async.} =
|
proc processInput(client: AsyncSocket) {.async.} =
|
||||||
while true:
|
while true:
|
||||||
let input = await asyncReadline()
|
let input = await asyncReadline()
|
||||||
await client.send(input & "\c\L")
|
await client.send(input & "\c\L")
|
||||||
|
|
||||||
proc runClient() {.async.} =
|
proc connectAsClient(clientAddress: string,
|
||||||
|
clientPort: Port,
|
||||||
|
serverAddress: string,
|
||||||
|
serverPort: Port) {.async.} =
|
||||||
var client = newAsyncSocket()
|
var client = newAsyncSocket()
|
||||||
client.setSockOpt(OptReuseAddr, true)
|
client.setSockOpt(OptReuseAddr, true)
|
||||||
client.getFd().setSockOptInt(IPPROTO_IP, IP_TTL, 2)
|
client.getFd().setSockOptInt(IPPROTO_IP, IP_TTL, 2)
|
||||||
client.bindAddr(Port(1234))
|
client.bindAddr(clientPort, clientAddress)
|
||||||
try:
|
try:
|
||||||
await client.connect("127.0.0.1", Port(4321))
|
echo "trying to connect"
|
||||||
|
await client.connect(serverAddress, serverPort)
|
||||||
let (address, port) = client.getPeerAddr()
|
let (address, port) = client.getPeerAddr()
|
||||||
echo("connected to $#:$#!".format(address, port.uint16))
|
echo("connected to $#:$#!".format(address, port.uint16))
|
||||||
asyncCheck processInput(client)
|
asyncCheck processInput(client)
|
||||||
|
@ -45,12 +48,15 @@ proc processClient(client: AsyncSocket) {.async.} =
|
||||||
echo("client sent message: $#".format(line))
|
echo("client sent message: $#".format(line))
|
||||||
await client.send(line & "\c\L")
|
await client.send(line & "\c\L")
|
||||||
|
|
||||||
proc runServer() {.async.} =
|
proc runServer(serverAddress: string,
|
||||||
|
serverPort: Port,
|
||||||
|
clientAddress: string,
|
||||||
|
clientPort: Port) {.async.} =
|
||||||
var server = newAsyncSocket()
|
var server = newAsyncSocket()
|
||||||
server.setSockOpt(OptReuseAddr, true)
|
server.setSockOpt(OptReuseAddr, true)
|
||||||
server.bindAddr(Port(4321))
|
server.bindAddr(serverPort)
|
||||||
server.listen()
|
server.listen()
|
||||||
echo("listening on port 4321")
|
echo("listening on port $#".format(serverPort.uint16))
|
||||||
while true:
|
while true:
|
||||||
let client = await server.accept()
|
let client = await server.accept()
|
||||||
let (address, port) = client.getPeerAddr()
|
let (address, port) = client.getPeerAddr()
|
||||||
|
@ -61,17 +67,29 @@ proc main() =
|
||||||
if paramCount() != 1:
|
if paramCount() != 1:
|
||||||
usage()
|
usage()
|
||||||
return
|
return
|
||||||
|
let serverAddress = "176.95.209.87"
|
||||||
|
let serverPort = Port(80)
|
||||||
|
let clientAddress = $getPrimaryIPAddr(parseIpAddress(serverAddress))
|
||||||
|
let clientPort = Port(1234)
|
||||||
case paramStr(1)
|
case paramStr(1)
|
||||||
of "client":
|
of "client":
|
||||||
asyncCheck runClient()
|
asyncCheck punchHoleAsClient(clientAddress, clientPort, serverAddress, serverPort)
|
||||||
|
asyncCheck connectAsClient(clientAddress, clientPort, serverAddress, serverPort)
|
||||||
|
try:
|
||||||
|
runForever()
|
||||||
|
except NetworkInterfaceError as e:
|
||||||
|
echo(e.msg)
|
||||||
|
except PunchHoleError as e:
|
||||||
|
echo(e.msg)
|
||||||
of "server":
|
of "server":
|
||||||
asyncCheck runServer()
|
asyncCheck runServer(serverAddress, serverPort, clientAddress, clientPort)
|
||||||
|
try:
|
||||||
|
runForever()
|
||||||
|
except ValueError:
|
||||||
|
echo "server got ValueError!"
|
||||||
|
discard
|
||||||
else:
|
else:
|
||||||
usage()
|
usage()
|
||||||
try:
|
|
||||||
runForever()
|
|
||||||
except ValueError:
|
|
||||||
discard
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in New Issue