From b714e699fb59d9c08e2a28876038df4eac755e40 Mon Sep 17 00:00:00 2001 From: "simon.kagstrom" Date: Sat, 7 Feb 2009 11:08:50 +0000 Subject: [PATCH] lots of very broken network stuff --- Src/Network.cpp | 89 +++++++++++++++++---- Src/Network.h | 76 +++++++++++++++--- Src/NetworkUnix.h | 16 ++++ Src/network-broker/main.py | 160 +++++++++++++++++++++++++++++++++++++ 4 files changed, 315 insertions(+), 26 deletions(-) create mode 100644 Src/network-broker/main.py diff --git a/Src/Network.cpp b/Src/Network.cpp index bb75e4d..e0ea412 100644 --- a/Src/Network.cpp +++ b/Src/Network.cpp @@ -68,6 +68,11 @@ Network::Network(int sock, bool is_master) /* Assume black screen */ memset(this->screen, 0, DISPLAY_X * DISPLAY_Y); + + /* Peer addresses */ + memset(&this->private_addr, 0, sizeof(struct sockaddr_in)); + memset(&this->public_addr, 0, sizeof(struct sockaddr_in)); + this->connection_addr = &this->public_addr; } Network::~Network() @@ -487,15 +492,17 @@ bool Network::ReceiveUpdate() bool out; memset(&tv, 0, sizeof(tv)); - out = this->ReceiveUpdate(this->ud, &tv); + out = this->ReceiveUpdate(this->ud, NETWORK_UPDATE_SIZE, &tv); return out; } -bool Network::ReceiveUpdate(NetworkUpdate *dst, struct timeval *tv) +bool Network::ReceiveUpdate(NetworkUpdate *dst, size_t total_sz, + struct timeval *tv) { Uint8 *pp = (Uint8*)dst; NetworkUpdate *p; + size_t sz_left = total_sz; if (this->Select(this->sock, tv) == false) return false; @@ -503,28 +510,25 @@ bool Network::ReceiveUpdate(NetworkUpdate *dst, struct timeval *tv) do { p = (NetworkUpdate*)pp; + size_t actual_sz; - /* Receive the header */ - if (this->ReceiveData((void*)p, this->sock, sizeof(NetworkUpdate)) == false) + if (sz_left <= 0) + return false; + + /* Receive the header */ + actual_sz = this->ReceiveFrom(pp, this->sock, + sz_left, this->connection_addr); + if (actual_sz < 0) return false; - pp = pp + sizeof(NetworkUpdate); /* Drop if the magic is wrong */ if (ntohs(p->magic) != FRODO_NETWORK_MAGIC) return false; - /* And the rest of the update */ - size_t sz = ntohl(p->size); - if (sz > sizeof(NetworkUpdate)) - { - size_t sz_diff = sz - sizeof(NetworkUpdate); - - if (this->ReceiveData((void*)pp, this->sock, sz_diff) == false) - return false; - pp = pp + sz_diff; - } if (this->DeMarshalData(p) == false) return false; + sz_left -= actual_sz; + pp = pp + actual_sz; } while ( !(p->type == STOP) ); return true; @@ -549,7 +553,7 @@ bool Network::SendUpdate() sz = this->GetNetworkUpdateSize(); if (sz <= 0) return false; - if (this->SendData((void*)src, this->sock, sz) == false) + if (this->SendTo((void*)src, this->sock, sz, this->connection_addr) < 0) return false; this->traffic += sz; @@ -564,6 +568,13 @@ void Network::AddNetworkUpdate(NetworkUpdate *update) this->cur_ud = (NetworkUpdate*)next; } +void Network::MangleIp(uint8 *ip) +{ + ip[0] = ~ip[0]; + ip[1] = ~ip[1]; + ip[2] = ~ip[2]; + ip[3] = ~ip[3]; +} bool Network::MarshalData(NetworkUpdate *p) { @@ -578,6 +589,24 @@ bool Network::MarshalData(NetworkUpdate *p) case DISCONNECT: case STOP: break; + case LIST_PEERS: + { + NetworkUpdateListPeers *lp = (NetworkUpdateListPeers *)p->data; + for (int i = 0; i < lp->n_peers; i++) + { + NetworkUpdatePeerInfo *peer = &lp->peers[i]; + + peer->key = htons(peer->key); + peer->private_port = htons(peer->private_port); + peer->public_port = htons(peer->public_port); + peer->is_master = htons(peer->is_master); + this->MangleIp(peer->private_ip); + this->MangleIp(peer->public_ip); + } + lp->n_peers = htonl(lp->n_peers); + this->MangleIp(lp->your_ip); + lp->your_port = htons(lp->your_port); + } break; default: /* Unknown data... */ fprintf(stderr, "Got unknown data %d while marshalling. Something is wrong\n", @@ -630,6 +659,25 @@ bool Network::DeMarshalData(NetworkUpdate *p) case STOP: /* Nothing to do, just bytes */ break; + case LIST_PEERS: + { + NetworkUpdateListPeers *lp = (NetworkUpdateListPeers *)p->data; + + lp->n_peers = ntohl(lp->n_peers); + for (int i = 0; i < lp->n_peers; i++) + { + NetworkUpdatePeerInfo *peer = &lp->peers[i]; + + peer->key = ntohs(peer->key); + peer->private_port = ntohs(peer->private_port); + peer->public_port = ntohs(peer->public_port); + peer->is_master = ntohs(peer->is_master); + this->MangleIp(peer->private_ip); + this->MangleIp(peer->public_ip); + } + this->MangleIp(lp->your_ip); + lp->your_port = ntohs(lp->your_port); + } break; default: /* Unknown data... */ return false; @@ -664,6 +712,15 @@ bool Network::DecodeUpdate(uint8 *screen, uint8 *js) *js = j->val; } break; + case LIST_PEERS: + { + NetworkUpdateListPeers *lp = (NetworkUpdateListPeers *)p->data; + + } break; + case PING: + /* Send an ack */ + break; + case ACK: /* Should never receive this */ case DISCONNECT: out = false; break; diff --git a/Src/Network.h b/Src/Network.h index b08b933..2eacea2 100644 --- a/Src/Network.h +++ b/Src/Network.h @@ -15,10 +15,16 @@ #define NETWORK_SOUND_BUF_SIZE 1024 enum { - STOP = 99, - LIST_PEERS = 88, - DISCONNECT = 77, - PEER_INFO = 66, + /* Connection-related messages */ + HELLO = 99, /* Hello, broker */ + LIST_PEERS = 98, /* List of peers */ + PEER_CONNECT = 97, /* A peer wants to connect */ + DISCONNECT = 96, /* Disconnect from a peer */ + PING = 95, /* (broker) are you alive? */ + ACK = 94, /* Answer to broker */ + /* Non-data messages */ + STOP = 55, /* End of this update sequence */ + /* Data transfer of various kinds */ DISPLAY_UPDATE_RAW = 1, DISPLAY_UPDATE_RLE = 2, DISPLAY_UPDATE_DIFF= 3, @@ -36,20 +42,57 @@ struct NetworkUpdate uint32 size; /* The rest is just data of some type */ - Uint8 data[]; + uint8 data[]; }; struct NetworkUpdateDisplay { - Uint8 square; - Uint8 data[]; + uint8 square; + uint8 data[]; }; struct NetworkUpdateJoystick { - Uint8 val; + uint8 val; }; +struct NetworkUpdatePingAck +{ + uint8 seq; +}; + +/* + * Sent by the third-party broker server when someone wants to connect + * to this machine. + * + * See http://www.brynosaurus.com/pub/net/p2pnat/ for how the UDP hole + * punching actually works. + */ +struct NetworkUpdatePeerInfo +{ + uint16 private_port; + uint16 public_port; + + uint8 private_ip[4]; /* Wii isn't ipv6 capable anyway AFAIK */ + uint8 public_ip[4]; + + uint16 key; /* Random value to separate same names */ + uint16 is_master; + uint8 name[32]; /* "SIMON", "LINDA" etc */ +}; + +struct NetworkUpdateListPeers +{ + uint32 n_peers; + uint8 your_ip[4]; + uint16 your_port; + uint8 d[2]; /* Pad to 4 bytes */ + + /* Followed by the actual peers */ + NetworkUpdatePeerInfo peers[]; +}; + + static inline NetworkUpdate *InitNetworkUpdate(NetworkUpdate *ud, uint16 type, uint32 size) { ud->magic = FRODO_NETWORK_MAGIC; @@ -73,7 +116,7 @@ public: void EncodeJoystickUpdate(Uint8 v); - bool DecodeUpdate(uint8 *screen, uint8 *js = NULL); + bool DecodeUpdate(uint8 *screen, uint8 *js); void ResetNetworkUpdate(void); @@ -195,14 +238,23 @@ protected: bool DecodeDisplayRaw(Uint8 *screen, struct NetworkUpdate *src, int x, int y); - bool ReceiveUpdate(NetworkUpdate *dst, struct timeval *tv); + bool ReceiveUpdate(NetworkUpdate *dst, size_t sz, struct timeval *tv); bool ReceiveData(void *dst, int sock, size_t sz); + /* Simple wrapper around our friend recvfrom */ + ssize_t ReceiveFrom(void *dst, int sock, size_t sz, + struct sockaddr_in *from); + + ssize_t SendTo(void *src, int sock, size_t sz, + struct sockaddr_in *to); + bool SendData(void *src, int sock, size_t sz); virtual bool Select(int sock, struct timeval *tv); + void MangleIp(uint8 *ip); + bool MarshalData(NetworkUpdate *ud); bool MarshalAllData(NetworkUpdate *p); @@ -236,6 +288,10 @@ protected: /* Connection to the peer */ int sock; + struct sockaddr_in private_addr; + struct sockaddr_in public_addr; + struct sockaddr_in *connection_addr; /* Points to either of the above */ + /* Listener-related */ static int listen_sock; diff --git a/Src/NetworkUnix.h b/Src/NetworkUnix.h index b39791d..1e51870 100644 --- a/Src/NetworkUnix.h +++ b/Src/NetworkUnix.h @@ -174,6 +174,22 @@ bool Network::ReceiveData(void *dst, int sock, size_t sz) return sz > 0; } +ssize_t Network::ReceiveFrom(void *dst, int sock, size_t sz, + struct sockaddr_in *from) +{ + socklen_t from_sz = from ? sizeof(struct sockaddr_in) : 0; + + return recvfrom(sock, dst, sz, 0, (struct sockaddr*)from, &from_sz); +} + +ssize_t Network::SendTo(void *src, int sock, size_t sz, struct sockaddr_in *to) +{ + socklen_t to_sz = sizeof(struct sockaddr_in); + + assert(to); + return sendto(sock, src, sz, 0, (struct sockaddr*)to, to_sz); +} + bool Network::SendData(void *src, int sock, size_t sz) { size_t sent_sz = 0; diff --git a/Src/network-broker/main.py b/Src/network-broker/main.py new file mode 100644 index 0000000..fa70d60 --- /dev/null +++ b/Src/network-broker/main.py @@ -0,0 +1,160 @@ +import socket +import SocketServer +import struct + +FRODO_NETWORK_MAGIC = 0x1976 + +HELLO = 99 +LIST_PEERS = 98 +PEER_CONNECT = 97 +DISCONNECT = 96 +PING = 95 +ACK = 94 +STOP = 55 + +# Some of the Frodo network packets. There are more packets, but these +# are not interesting to the broker (and shouldn't be sent there either!) +packet_class_by_type = { + HELLO : HelloPacket, + LIST_PEERS : ListPeersPacket, + PEER_CONNECT : ListPeersPacket, + DISCONNECT : DisconnectPacket, + + PING : PingPacket, + ACK : PingPacket, # Ping and ack are the same + + STOP : StopPacket, +} + +class Packet: + def __init__(self, data): + """Create a new packet from raw data. Data should always be in network +byte order""" + self.magic = struct.unpack(">H", data, offset = 0)[0] + self.type = struct.unpack(">H", data, offset = 2)[0] + self.size = struct.unpack(">L", data, offset = 4)[0] + + def __init__(self, magic, type, size): + """Create a new packet""" + self.magic = magic + self.type = type + self.size = size + + def get_magic(self): + return self.magic + + def get_type(self): + return self.type + + def get_size(self): + return self.size + + def marshal(self): + return struct.pack(">HHL", self.magic, self.type, self.size) + +class PingPacket(Packet): + def __init__(self, seq): + Packet.__init__(self, FRODO_NETWORK_MAGIC, TYPE_PING, 9) + self.seq = seq + + def get_seq(self): + return self.seq + + def marshal(self): + return Packet.marshal(self) + struct.pack(">B", self.seq) + +class PeerInfo: + def __init__(self, data): + self.private_port = struct.unpack(">H", data, offset = 0)[0] + self.public_port = struct.unpack(">H", data, offset = 2)[0] + self.private_ip = self.mangle_ip(struct.unpack(">BBBB", data, offset = 4)) + self.public_ip = self.mangle_ip(struct.unpack(">BBBB", data, offset = 8)) + self.key = struct.unpack(">H", data, offset = 12)[0] + self.is_master = struct.unpack(">H", data, offset = 16)[0] + self.name = struct.unpack("32s", data, offset = 20) + self.name[31] = 0 + + def set_public_address(ip, port): + self.public_ip = ip + self.public_port = port + + def mangle_ip(self, ip): + ip[0] = ~int(ip[0]) + ip[1] = ~int(ip[1]) + ip[2] = ~int(ip[2]) + ip[3] = ~int(ip[3]) + + def marshal(self): + priv_ip = self.mangle_ip(self.private_ip) + pub_ip = self.mangle_ip(self.public_ip) + return struct.pack(">HH4B4BHH32s", + self.private_port, self.public_port, priv_ip, pub_ip, + self.key, self.is_master, self.name) + +class HelloPacket(Packet): + def __init__(self, data): + Packet.__init__(self, data) + + self.peer_info = PeerInfo(data[8:]) + + def marshal(self): + return Packet.marshal(self) + self.peer_info.marshal() + +class Peer: + def __init__(self, key, srv): + self.hash_key = key + self.srv = srv + + def get_hash_key(self): + return self.hash_key + + def handle_packet(self, data): + raw = Packet(data) + pkg_cls = packet_class_by_type[raw.get_type()] + pkg = pkg_cls(data) + + if pkg.type == ACK: + pass + elif pkg.type == HELLO: + pass + elif pkg.type == PEER_CONNECT: + pass + +class BrokerPacketHandler(SocketServer.DatagramRequestHandler): + def handle(self): + srv = self.server + msg = self.rfile.read() + + peer = srv.get_peer(self.client_addr) + try: + reply = peer.handle_packet() + except: + # Sends crap, let's remove it + srv.remove_peer(peer) + + if reply != None: + self.wfile.write(reply) + +class Broker(SocketServer.UDPServer): + + def __init__(self, host, req_handler): + SocketServer.UDPServer.__init__(self, host, req_handler) + # Instead of setsockopt( ... REUSEADDR ... ) + self.allow_reuse_address = True + self.peers = {} + + def get_peer(self, key): + "Return the peer for a certain key, or a new one if it doesn't exist" + try: + peer = self.peers[key] + except KeyError, e: + peer = Peer(key, self) + return peer + + def remove_peer(self, peer): + del self.peers[ peer.get_hash_key() ] + +if __name__ == "__main__": + print "Starting Frodo network broker" + s = Broker( ("localhost", 46214), BrokerPacketHandler) + s.serve_forever()