/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.network.accesspoint; import static jpcsp.HLE.modules.sceNetAdhoc.ANY_MAC_ADDRESS; import static jpcsp.HLE.modules.sceWlan.WLAN_CMD_DATA; import static jpcsp.hardware.Wlan.MAC_ADDRESS_LENGTH; import static jpcsp.network.protocols.ARP.ARP_OPERATION_REPLY; import static jpcsp.network.protocols.ARP.ARP_OPERATION_REQUEST; import static jpcsp.network.protocols.DHCP.DHCP_BOOT_REPLY; import static jpcsp.network.protocols.DNS.DNS_RESPONSE_CODE_NAME_ERROR; import static jpcsp.network.protocols.DNS.DNS_RESPONSE_CODE_NO_ERROR; import static jpcsp.network.protocols.EtherFrame.ETHER_TYPE_ARP; import static jpcsp.network.protocols.EtherFrame.ETHER_TYPE_IPv4; import static jpcsp.network.protocols.ICMP.ICMP_CONTROL_ECHO_REQUEST; import static jpcsp.network.protocols.IPv4.IPv4_PROTOCOL_ICMP; import static jpcsp.network.protocols.IPv4.IPv4_PROTOCOL_TCP; import static jpcsp.network.protocols.IPv4.IPv4_PROTOCOL_UDP; import static jpcsp.network.protocols.NetPacket.getIpAddressString; import static jpcsp.network.protocols.UDP.UDP_PORT_DNS; import static jpcsp.util.Utilities.writeStringNZ; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Random; import jpcsp.HLE.kernel.types.pspNetMacAddress; import jpcsp.HLE.modules.sceNetApctl; import jpcsp.HLE.modules.sceNetInet; import jpcsp.HLE.modules.sceWlan; import jpcsp.network.protocols.ARP; import jpcsp.network.protocols.DHCP; import jpcsp.network.protocols.DNS; import jpcsp.network.protocols.DNS.DNSAnswerRecord; import jpcsp.network.protocols.EtherFrame; import jpcsp.network.protocols.ICMP; import jpcsp.network.protocols.IPv4; import jpcsp.network.protocols.NetPacket; import jpcsp.network.protocols.TCP; import jpcsp.network.protocols.UDP; import jpcsp.network.protocols.DNS.DNSRecord; import jpcsp.util.Utilities; import org.apache.log4j.Logger; public class AccessPoint { public static Logger log = Logger.getLogger("accesspoint"); public static final int HARDWARE_TYPE_ETHERNET = 0x0001; public static final int IP_ADDRESS_LENGTH = 4; private static final int BUFFER_SIZE = 2000; private static AccessPoint instance; private int apSocketPort = 30020; private pspNetMacAddress apMacAddress; private byte[] apIpAddress; private byte[] localIpAddress; private DatagramSocket apSocket; private AccessPointThread apThread; private String apSsid; private List<TcpConnectionState> tcpConnectionStates; private Random random; private static class TcpConnectionState { public pspNetMacAddress sourceMacAddress; public byte[] sourceIPAddress; public int sourcePort; public int sourceSequenceNumber; public pspNetMacAddress destinationMacAddress; public byte[] destinationIPAddress; public int destinationPort; public int destinationSequenceNumber; public SocketChannel socketChannel; public byte[] pendingWriteData; private void openChannel() throws IOException { if (socketChannel == null) { socketChannel = SocketChannel.open(); // Use a non-blocking channel as we are polling for data socketChannel.configureBlocking(false); // Connect has no timeout socketChannel.socket().setSoTimeout(0); } } public void connect() throws IOException { openChannel(); if (!socketChannel.isConnected() && !socketChannel.isConnectionPending()) { SocketAddress socketAddress = new InetSocketAddress(InetAddress.getByAddress(destinationIPAddress), destinationPort); socketChannel.connect(socketAddress); } } public void write(byte[] buffer) throws IOException { if (buffer != null) { write(buffer, 0, buffer.length); } } public void write(byte[] buffer, int offset, int length) throws IOException { socketChannel.write(ByteBuffer.wrap(buffer, 0, length)); } public byte[] read() throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; int length = socketChannel.read(ByteBuffer.wrap(buffer)); if (length <= 0) { return null; } byte[] readBuffer = new byte[length]; System.arraycopy(buffer, 0, readBuffer, 0, length); return readBuffer; } public void addPendingWriteData(byte[] data) { if (data != null && data.length > 0) { pendingWriteData = Utilities.extendArray(pendingWriteData, data); } } @Override public String toString() { return String.format("source=%s/%s:0x%X(sequenceNumber=0x%X), destination=%s/%s:0x%X(sequenceNumber=0x%X)", sourceMacAddress, getIpAddressString(sourceIPAddress), sourcePort, sourceSequenceNumber, destinationMacAddress, getIpAddressString(destinationIPAddress), destinationPort, destinationSequenceNumber); } } private class AccessPointThread extends Thread { private boolean exit = false; @Override public void run() { while (!exit) { if (!receiveAccessPointMessage()) { if (!exit && !receiveTcpMessages()) { Utilities.sleep(10, 0); } } } } public void exit() { exit = true; } } public static AccessPoint getInstance() { if (instance == null) { instance = new AccessPoint(); } return instance; } private AccessPoint() { // Generate a random MAC address for the Address Point apMacAddress = new pspNetMacAddress(pspNetMacAddress.getRandomMacAddress()); apIpAddress = getIpAddress(sceNetApctl.getGateway()); localIpAddress = getIpAddress(sceNetApctl.getLocalHostIP()); tcpConnectionStates = new LinkedList<>(); random = new Random(); apThread = new AccessPointThread(); apThread.setDaemon(true); apThread.setName("Access Point Thread"); apThread.start(); if (log.isDebugEnabled()) { log.debug(String.format("AccessPoint using MAC=%s, IP=%s", apMacAddress, getIpAddressString(apIpAddress))); } } public static void exit() { if (instance != null) { if (instance.apThread != null) { instance.apThread.exit(); instance.apThread = null; } } } public int getPort() { return apSocketPort; } public pspNetMacAddress getMacAddress() { return apMacAddress; } public byte[] getIpAddress() { return apIpAddress; } private byte[] getLocalIpAddress() { return localIpAddress; } private static byte[] getIpAddress(String hostName) { try { InetAddress inetAddress = InetAddress.getByName(hostName); return inetAddress.getAddress(); } catch (UnknownHostException e) { log.error("getIpAddress", e); } return null; } private static byte[] getIpAddress(int ipAddressInt) { byte[] ipAddress = new byte[IP_ADDRESS_LENGTH]; ipAddress[0] = (byte) (ipAddressInt >> 24); ipAddress[1] = (byte) (ipAddressInt >> 16); ipAddress[2] = (byte) (ipAddressInt >> 8); ipAddress[3] = (byte) ipAddressInt; return ipAddress; } private boolean createAccessPointSocket() { if (apSocket == null) { boolean retry; do { retry = false; try { apSocket = new DatagramSocket(apSocketPort); // For broadcast apSocket.setBroadcast(true); // Non-blocking (timeout = 0 would mean blocking) apSocket.setSoTimeout(1); } catch (BindException e) { if (log.isDebugEnabled()) { log.debug(String.format("createAccessPointSocket port %d already in use (%s) - retrying with port %d", apSocketPort, e, apSocketPort + 1)); } // The port is already busy, retrying with another port apSocketPort++; retry = true; } catch (SocketException e) { log.error("createWlanSocket", e); } } while (retry); } return apSocket != null; } private boolean receiveAccessPointMessage() { boolean packetReceived = false; if (!createAccessPointSocket()) { return packetReceived; } byte[] bytes = new byte[10000]; DatagramPacket packet = new DatagramPacket(bytes, bytes.length); try { apSocket.receive(packet); if (log.isDebugEnabled()) { log.debug(String.format("receiveMessage message: %s", Utilities.getMemoryDump(packet.getData(), packet.getOffset(), packet.getLength()))); } packetReceived = true; byte[] dataBytes = packet.getData(); int dataOffset = packet.getOffset(); int dataLength = packet.getLength(); NetPacket netPacket = new NetPacket(dataBytes, dataOffset, dataLength); processMessage(netPacket); } catch (SocketTimeoutException e) { // Timeout can be ignored as we are polling } catch (IOException e) { log.error("receiveMessage", e); } return packetReceived; } private int getBroadcastPort() { return sceWlan.getSocketPort(); } private void sendPacket(byte[] buffer, int bufferLength) { if (log.isDebugEnabled()) { log.debug(String.format("sendPacket %s", Utilities.getMemoryDump(buffer, 0, bufferLength))); } try { InetSocketAddress broadcastAddress[] = sceNetInet.getBroadcastInetSocketAddress(getBroadcastPort()); if (broadcastAddress != null) { for (int i = 0; i < broadcastAddress.length; i++) { DatagramPacket packet = new DatagramPacket(buffer, bufferLength, broadcastAddress[i]); apSocket.send(packet); } } } catch (UnknownHostException e) { log.error("sendPacket", e); } catch (IOException e) { log.error("sendPacket", e); } } private void sendPacket(NetPacket packet) { int packetLength = packet.getOffset(); byte[] buffer = new byte[33 + packetLength]; int offset = 0; buffer[offset++] = WLAN_CMD_DATA; writeStringNZ(buffer, offset, 32, apSsid); offset += 32; System.arraycopy(packet.getBuffer(), 0, buffer, offset, packetLength); offset += packetLength; sendPacket(buffer, offset); } private void processMessage(NetPacket packet) throws EOFException { byte cmd = packet.readByte(); if (cmd != WLAN_CMD_DATA) { log.error(String.format("processMessage unknown command 0x%X", cmd)); return; } String ssid = packet.readStringNZ(32); if (log.isDebugEnabled()) { log.debug(String.format("processMessage ssid='%s'", ssid)); } if (apSsid == null) { apSsid = ssid; if (log.isDebugEnabled()) { log.debug(String.format("Using ssid='%s' for the Access Point", apSsid)); } } EtherFrame frame = new EtherFrame(); frame.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessage %s", frame)); } switch (frame.type) { case ETHER_TYPE_ARP: processMessageARP(packet); break; case ETHER_TYPE_IPv4: // See https://www.ietf.org/rfc/rfc894.txt processMessageDatagram(packet, frame); break; default: log.warn(String.format("Unknow message of type 0x%04X", frame.type)); break; } } private void processMessageARP(NetPacket packet) throws EOFException { ARP arp = new ARP(); arp.read(packet); if (arp.hardwareType != HARDWARE_TYPE_ETHERNET) { log.warn(String.format("processMessageARP unknown hardwareType=0x%X", arp.hardwareType)); return; } if (arp.protocolType != ETHER_TYPE_IPv4) { log.warn(String.format("processMessageARP unknown protocolType=0x%X", arp.protocolType)); return; } if (arp.hardwareAddressLength != MAC_ADDRESS_LENGTH) { log.warn(String.format("processMessageARP unknown hardwareAddressLength=0x%X", arp.protocolType)); return; } if (arp.protocolAddressLength != IP_ADDRESS_LENGTH) { log.warn(String.format("processMessageARP unknown protocolAddressLength=0x%X", arp.protocolType)); return; } if (arp.operation != ARP_OPERATION_REQUEST && arp.operation != ARP_OPERATION_REPLY) { log.warn(String.format("processMessageARP unknown operation=0x%X", arp.operation)); return; } if (log.isDebugEnabled()) { log.debug(String.format("processMessageARP %s", arp)); } if (arp.targetHardwareAddress.isEmptyMacAddress()) { // A gratuitous ARP message has been received. // It is used to announce a new IP address. // Send back a gratuitous ARP message to announce ourself. sendGratuitousARP(); } } private void sendGratuitousARP() throws EOFException { EtherFrame frame = new EtherFrame(); frame.dstMac = new pspNetMacAddress(ANY_MAC_ADDRESS); frame.srcMac = getMacAddress(); frame.type = ETHER_TYPE_ARP; ARP arp = new ARP(); arp.hardwareType = HARDWARE_TYPE_ETHERNET; arp.protocolType = ETHER_TYPE_IPv4; arp.hardwareAddressLength = MAC_ADDRESS_LENGTH; arp.protocolAddressLength = IP_ADDRESS_LENGTH; arp.operation = ARP_OPERATION_REQUEST; arp.senderHardwareAddress = getMacAddress(); arp.senderProtocolAddress = getIpAddress(); // Set the target hardware address to 00:00:00:00:00:00 arp.targetHardwareAddress = new pspNetMacAddress(); arp.targetProtocolAddress = getIpAddress(); NetPacket packet = new NetPacket(EtherFrame.sizeOf() + arp.sizeOf()); frame.write(packet); arp.write(packet); sendPacket(packet); } private void processMessageDatagram(NetPacket packet, EtherFrame frame) throws EOFException { IPv4 ipv4 = new IPv4(); ipv4.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessageDatagram IPv4 %s", ipv4)); } switch (ipv4.protocol) { case IPv4_PROTOCOL_ICMP: processMessageDatagramICMP(packet, frame, ipv4); break; case IPv4_PROTOCOL_TCP: processMessageTCP(packet, frame, ipv4); break; case IPv4_PROTOCOL_UDP: processMessageUDP(packet, frame, ipv4); break; default: log.warn(String.format("processMessageDatagram unknown protocol %d", ipv4.protocol)); break; } } private void processMessageUDP(NetPacket packet, EtherFrame frame, IPv4 ipv4) throws EOFException { UDP udp = new UDP(); udp.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessageUDP %s", udp)); } switch (udp.destinationPort) { case UDP_PORT_DNS: processMessageDNS(packet, frame, ipv4, udp); break; case UDP.UDP_PORT_DHCP_SERVER: processMessageDHCP(packet, frame, ipv4, udp); break; default: log.warn(String.format("processMessageUDP unknown destination port 0x%X", udp.destinationPort)); break; } } private void processMessageDNS(NetPacket packet, EtherFrame frame, IPv4 ipv4, UDP udp) throws EOFException { DNS dns = new DNS(); dns.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessageDNS %s", dns)); } if (!dns.isResponseFlag && dns.questionCount == 1) { DNSRecord question = dns.questions[0]; String hostName = question.recordName; DNS answerDns = new DNS(dns); try { InetAddress inetAddress = InetAddress.getByName(hostName); if (log.isDebugEnabled()) { log.debug(String.format("DNS response '%s'=%s", hostName, inetAddress)); } DNSAnswerRecord answer = new DNSAnswerRecord(); answer.recordName = hostName; answer.recordClass = question.recordClass; answer.recordType = question.recordType; answer.data = inetAddress.getAddress(); answer.dataLength = answer.data.length; answerDns.responseCode = DNS_RESPONSE_CODE_NO_ERROR; answerDns.answerRecordCount = 1; answerDns.answerRecords = new DNSAnswerRecord[] { answer }; } catch (UnknownHostException e) { answerDns.responseCode = DNS_RESPONSE_CODE_NAME_ERROR; if (log.isDebugEnabled()) { log.debug(String.format("processMessageDNS unknown host '%s'(%s)", hostName, e.toString())); } } answerDns.isResponseFlag = true; EtherFrame answerFrame = new EtherFrame(frame); answerFrame.swapSourceAndDestination(); IPv4 answerIPv4 = new IPv4(ipv4); answerIPv4.swapSourceAndDestination(); answerIPv4.timeToLive--; // When a packet arrives at a router, the router decreases the TTL field. UDP answerUdp = new UDP(udp); answerUdp.swapSourceAndDestination(); // Update lengths and checksums answerUdp.length = answerUdp.sizeOf() + answerDns.sizeOf(); answerUdp.computeChecksum(); answerIPv4.totalLength = answerIPv4.sizeOf() + answerUdp.length; answerIPv4.computeChecksum(); // Write the different headers in sequence NetPacket answerPacket = new NetPacket(BUFFER_SIZE); answerFrame.write(answerPacket); answerIPv4.write(answerPacket); answerUdp.write(answerPacket); answerDns.write(answerPacket); sendPacket(answerPacket); } } private void processMessageDatagramICMP(NetPacket packet, EtherFrame frame, IPv4 ipv4) throws EOFException { ICMP icmp = new ICMP(); icmp.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessageDatagramICMP %s", icmp)); } switch (icmp.type) { case ICMP_CONTROL_ECHO_REQUEST: sendICMPEchoResponse(packet, frame, ipv4, icmp); break; default: log.warn(String.format("processMessageDatagramICMP unknown type=0x%X, code=0x%X", icmp.type, icmp.code)); break; } } private void sendICMPEchoResponse(NetPacket packet, EtherFrame frame, IPv4 ipv4, ICMP icmp) throws EOFException { boolean reachable = false; try { InetAddress inetAddress = InetAddress.getByAddress(ipv4.destinationIPAddress); // Timeout after 1 second reachable = inetAddress.isReachable(null, ipv4.timeToLive, 1000); } catch (UnknownHostException e) { } catch (IOException e) { } if (reachable) { // See https://en.wikipedia.org/wiki/Ping_(networking_utility) EtherFrame answerFrame = new EtherFrame(frame); answerFrame.swapSourceAndDestination(); IPv4 answerIPv4 = new IPv4(ipv4); answerIPv4.swapSourceAndDestination(); answerIPv4.timeToLive--; // When a packet arrives at a router, the router decreases the TTL field. ICMP answerIcmp = new ICMP(icmp); answerIcmp.type = ICMP.ICMP_CONTROL_ECHO_REPLY; answerIcmp.computeChecksum(); answerIPv4.totalLength = answerIPv4.sizeOf() + answerIcmp.sizeOf(); answerIPv4.computeChecksum(); // Write the different headers in sequence NetPacket answerPacket = new NetPacket(BUFFER_SIZE); answerFrame.write(answerPacket); answerIPv4.write(answerPacket); answerIcmp.write(answerPacket); sendPacket(answerPacket); } } private TcpConnectionState getTcpConnectionState(IPv4 ipv4, TCP tcp) { for (TcpConnectionState tcpConnectionState : tcpConnectionStates) { if ( tcp.sourcePort == tcpConnectionState.sourcePort && tcp.destinationPort == tcpConnectionState.destinationPort && Arrays.equals(ipv4.sourceIPAddress, tcpConnectionState.sourceIPAddress) && Arrays.equals(ipv4.destinationIPAddress, tcpConnectionState.destinationIPAddress) ) { return tcpConnectionState; } } // Not found return null; } private void processMessageTCP(NetPacket packet, EtherFrame frame, IPv4 ipv4) throws EOFException { TCP tcp = new TCP(); tcp.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessageTCP %s", tcp)); } TcpConnectionState tcpConnectionState = getTcpConnectionState(ipv4, tcp); if (tcp.flagSYN) { if (tcpConnectionState != null) { log.error(String.format("processMessageTCP SYN received but connection already exists: %s", tcpConnectionState)); return; } tcpConnectionState = new TcpConnectionState(); tcpConnectionState.sourceMacAddress = frame.srcMac; tcpConnectionState.destinationMacAddress = frame.dstMac; tcpConnectionState.sourceIPAddress = ipv4.sourceIPAddress; tcpConnectionState.destinationIPAddress = ipv4.destinationIPAddress; tcpConnectionState.sourcePort = tcp.sourcePort; tcpConnectionState.destinationPort = tcp.destinationPort; tcpConnectionState.sourceSequenceNumber = tcp.sequenceNumber; tcpConnectionState.destinationSequenceNumber = random.nextInt(); tcpConnectionStates.add(tcpConnectionState); tcpConnectionState.sourceSequenceNumber++; sendAcknowledgeTCP(frame, ipv4, tcp, tcpConnectionState); tcpConnectionState.destinationSequenceNumber++; } else if (tcp.flagACK) { if (tcpConnectionState == null) { // Acknowledge to an unknown connection, ignore if (log.isDebugEnabled()) { log.debug(String.format("processMessageTCP ACK received for unknown connection: %s", tcp)); } return; } try { if (tcp.flagFIN) { tcpConnectionState.sourceSequenceNumber++; sendAcknowledgeTCP(frame, ipv4, tcp, tcpConnectionState); } else if (tcp.flagPSH) { // Acknowledge the reception of the data sendAcknowledgeTCP(frame, ipv4, tcp, tcpConnectionState); // Connect and queue the received data for the destination tcpConnectionState.connect(); tcpConnectionState.addPendingWriteData(tcp.data); } } catch (IOException e) { log.error("processMessageTCP", e); } } } private void sendAcknowledgeTCP(EtherFrame frame, IPv4 ipv4, TCP tcp, TcpConnectionState tcpConnectionState) throws EOFException { tcpConnectionState.sourceSequenceNumber += tcp.data.length; EtherFrame answerFrame = new EtherFrame(frame); answerFrame.swapSourceAndDestination(); IPv4 answerIPv4 = new IPv4(ipv4); answerIPv4.swapSourceAndDestination(); answerIPv4.timeToLive--; // When a packet arrives at a router, the router decreases the TTL field. TCP answerTcp = new TCP(tcp); answerTcp.swapSourceAndDestination(); answerTcp.acknowledgmentNumber = tcpConnectionState.sourceSequenceNumber; answerTcp.sequenceNumber = tcpConnectionState.destinationSequenceNumber; answerTcp.flagPSH = false; answerTcp.flagACK = true; answerTcp.data = null; // Update lengths and checksums answerTcp.computeChecksum(answerIPv4); answerIPv4.totalLength = answerIPv4.sizeOf() + answerTcp.sizeOf(); answerIPv4.computeChecksum(); // Write the different headers in sequence NetPacket answerPacket = new NetPacket(BUFFER_SIZE); answerFrame.write(answerPacket); answerIPv4.write(answerPacket); answerTcp.write(answerPacket); if (log.isDebugEnabled()) { log.debug(String.format("processMessageTCP ACK frame=%s", answerFrame)); log.debug(String.format("processMessageTCP ACK IPv4=%s", answerIPv4)); log.debug(String.format("processMessageTCP ACK TCP=%s", answerTcp)); } sendPacket(answerPacket); } private void sendTcpData(TcpConnectionState tcpConnectionState, byte[] data) throws EOFException { EtherFrame answerFrame = new EtherFrame(); answerFrame.srcMac = tcpConnectionState.destinationMacAddress; answerFrame.dstMac = tcpConnectionState.sourceMacAddress; answerFrame.type = ETHER_TYPE_IPv4; IPv4 answerIPv4 = new IPv4(); answerIPv4.protocol = IPv4_PROTOCOL_TCP; answerIPv4.sourceIPAddress = tcpConnectionState.destinationIPAddress; answerIPv4.destinationIPAddress = tcpConnectionState.sourceIPAddress; TCP answerTcp = new TCP(); answerTcp.sourcePort = tcpConnectionState.destinationPort; answerTcp.destinationPort = tcpConnectionState.sourcePort; answerTcp.sequenceNumber = tcpConnectionState.destinationSequenceNumber; answerTcp.acknowledgmentNumber = tcpConnectionState.sourceSequenceNumber; answerTcp.flagACK = true; answerTcp.flagPSH = true; tcpConnectionState.destinationSequenceNumber += data.length; answerTcp.data = data; // Update lengths and checksums answerTcp.computeChecksum(answerIPv4); answerIPv4.totalLength = answerIPv4.sizeOf() + answerTcp.sizeOf(); answerIPv4.computeChecksum(); // Write the different headers in sequence NetPacket answerPacket = new NetPacket(BUFFER_SIZE); answerFrame.write(answerPacket); answerIPv4.write(answerPacket); answerTcp.write(answerPacket); if (log.isDebugEnabled()) { log.debug(String.format("sendTcpData frame=%s", answerFrame)); log.debug(String.format("sendTcpData IPv4=%s", answerIPv4)); log.debug(String.format("sendTcpData TCP=%s", answerTcp)); } sendPacket(answerPacket); } private boolean receiveTcpMessages() { boolean received = false; for (TcpConnectionState tcpConnectionState : tcpConnectionStates) { if (log.isTraceEnabled()) { log.trace(String.format("receiveTcpMessages polling %s", tcpConnectionState)); } SocketChannel socketChannel = tcpConnectionState.socketChannel; try { if (socketChannel != null && socketChannel.finishConnect()) { // Write any pending data byte[] pendingWriteData = tcpConnectionState.pendingWriteData; if (pendingWriteData != null) { tcpConnectionState.pendingWriteData = null; if (log.isDebugEnabled()) { log.debug(String.format("receiveTcpMessages sending pending write data: %s", Utilities.getMemoryDump(pendingWriteData))); } tcpConnectionState.write(pendingWriteData); } // Receive any available data byte[] receivedData = tcpConnectionState.read(); if (receivedData != null) { received = true; sendTcpData(tcpConnectionState, receivedData); } } } catch (IOException e) { // Ignore exceptions log.error("receiveTcpMessages", e); } } return received; } private void sendDHCPReply(EtherFrame frame, IPv4 ipv4, UDP udp, DHCP dhcp, int messageType) throws EOFException { // Send back a DHCP offer message EtherFrame answerFrame = new EtherFrame(frame); answerFrame.swapSourceAndDestination(); answerFrame.srcMac = getMacAddress(); IPv4 answerIPv4 = new IPv4(ipv4); answerIPv4.sourceIPAddress = getIpAddress(); answerIPv4.timeToLive--; // When a packet arrives at a router, the router decreases the TTL field. UDP answerUdp = new UDP(udp); answerUdp.swapSourceAndDestination(); DHCP answerDhcp = new DHCP(dhcp); answerDhcp.opcode = DHCP_BOOT_REPLY; answerDhcp.yourIPAddress = getLocalIpAddress(); answerDhcp.nextServerIPAddress = getIpAddress(); answerDhcp.clearOptions(); // The DHCP message type answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_MESSAGE_TYPE, (byte) messageType)); // The subnet mask answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_SUBNET_MASK, getIpAddress(sceNetApctl.getSubnetMaskInt()))); // The only router is myself answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_ROUTER, getIpAddress())); // The IP address lease time is forever answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_IP_ADDRESS_LEASE_TIME, Integer.MAX_VALUE)); // The DHCP server identification is myself answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_SERVER_IDENTIFIER, getIpAddress())); // The only DNS server is myself answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_DNS, getIpAddress())); // The broadcast address answerDhcp.addOption(new DHCP.DHCPOption(DHCP.DHCP_OPTION_BROADCAST_ADDRESS, DHCP.broadcastIPAddress)); // Update lengths and checksums answerUdp.length = answerUdp.sizeOf() + answerDhcp.sizeOf(); answerUdp.computeChecksum(); answerIPv4.totalLength = answerIPv4.sizeOf() + answerUdp.length; answerIPv4.computeChecksum(); // Write the different headers in sequence NetPacket answerPacket = new NetPacket(BUFFER_SIZE); answerFrame.write(answerPacket); answerIPv4.write(answerPacket); answerUdp.write(answerPacket); answerDhcp.write(answerPacket); if (log.isDebugEnabled()) { log.debug(String.format("sendDHCPReply frame=%s", answerFrame)); log.debug(String.format("sendDHCPReply IPv4=%s", answerIPv4)); log.debug(String.format("sendDHCPReply UDP=%s", answerUdp)); log.debug(String.format("sendDHCPReply messageType=%d, DHCP=%s", messageType, answerDhcp)); } sendPacket(answerPacket); } private void processMessageDHCP(NetPacket packet, EtherFrame frame, IPv4 ipv4, UDP udp) throws EOFException { DHCP dhcp = new DHCP(); dhcp.read(packet); if (log.isDebugEnabled()) { log.debug(String.format("processMessageDHCP %s", dhcp)); } if (dhcp.isDiscovery(udp, ipv4)) { // Send back a DHCP offset message sendDHCPReply(frame, ipv4, udp, dhcp, DHCP.DHCP_OPTION_MESSAGE_TYPE_DHCPOFFER); } else if (dhcp.isRequest(udp, ipv4, getLocalIpAddress())) { // Send back a DHCP acknowledgment message sendDHCPReply(frame, ipv4, udp, dhcp, DHCP.DHCP_OPTION_MESSAGE_TYPE_DHCPACK); } else { log.warn(String.format("Unknown DHCP request %s", dhcp)); } } }