summaryrefslogtreecommitdiff
path: root/misc/server
diff options
context:
space:
mode:
authorsjtufs <[email protected]>2013-08-25 15:45:55 +0800
committersjtufs <[email protected]>2013-08-25 15:45:55 +0800
commitac7633d8149a28af288ac0b850850cef9b13c151 (patch)
tree861fe3d57c114b5ec7f3141679428cc6302d21bc /misc/server
parenta53f4d21238d2bccd58a22d7485e54f495dbd55d (diff)
This is alpha
Diffstat (limited to 'misc/server')
-rw-r--r--misc/server/README.rst1
-rw-r--r--misc/server/client.py58
-rw-r--r--misc/server/piztor/exc.py17
-rw-r--r--misc/server/piztor/import.py44
-rw-r--r--misc/server/piztor/model.py79
-rw-r--r--misc/server/piztor_server.py234
-rw-r--r--misc/server/ptp.rst96
-rw-r--r--misc/server/rush.py14
8 files changed, 543 insertions, 0 deletions
diff --git a/misc/server/README.rst b/misc/server/README.rst
new file mode 100644
index 0000000..e88c745
--- /dev/null
+++ b/misc/server/README.rst
@@ -0,0 +1 @@
+Here is the folder of server-side implementation
diff --git a/misc/server/client.py b/misc/server/client.py
new file mode 100644
index 0000000..15f4bbc
--- /dev/null
+++ b/misc/server/client.py
@@ -0,0 +1,58 @@
+import socket
+import sys
+from struct import *
+from random import random
+from time import sleep
+
+def get_hex(data):
+ return "".join([hex(ord(c))[2:].zfill(2) for c in data])
+
+HOST, PORT = "localhost", 9990
+
+def gen_auth(username, password):
+ data = pack("!B", 0)
+ data += username
+ data += "\0"
+ data += password
+ return data
+
+def gen_update_location(token, lat, lont):
+ return pack("!BLdd", 2, token, lat, lont)
+
+def gen_request_location(token, gid):
+ return pack("!BLL", 3, token, gid)
+
+def send(data):
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((HOST, PORT))
+# print "Client " + str(sys.argv[1]) + ": connected"
+ sock.sendall(data)
+ print get_hex(data)
+# print "Client " + str(sys.argv[1]) + ": sent"
+# sock.shutdown(socket.SHUT_WR)
+# print "Client " + str(sys.argv[1]) + ": shutdown"
+ received = sock.recv(1024)
+ finally:
+ print "adf"
+ sock.close()
+
+ print "Sent {}".format(get_hex(data))
+ print "Received: {}".format(get_hex(data))
+ return received
+
+#print "Client spawned:" + str(sys.argv[1])
+rec = send(gen_auth("hello", "world"))
+opt, token, status = unpack("!BLB", rec)
+
+rec = send(gen_update_location(token, random(), random()))
+opc, status = unpack("!BB", rec)
+
+rec = send(gen_request_location(token, 1))
+opc, length = unpack("!BL", rec[:5])
+idx = 5
+for i in xrange(length):
+ uid, lat, lng = unpack("!Ldd", rec[idx:idx + 20])
+ print (uid, lat, lng)
+ idx += 20
+# sleep(60)
diff --git a/misc/server/piztor/exc.py b/misc/server/piztor/exc.py
new file mode 100644
index 0000000..2c53dbf
--- /dev/null
+++ b/misc/server/piztor/exc.py
@@ -0,0 +1,17 @@
+class PiztorError(Exception):
+ pass
+
+class DBCurruptedError(PiztorError):
+ pass
+
+class ConnectionError(PiztorError):
+ pass
+
+class ReqReadError(ConnectionError):
+ pass
+
+class BadReqError(ConnectionError):
+ pass
+
+class BadTokenError(ConnectionError):
+ pass
diff --git a/misc/server/piztor/import.py b/misc/server/piztor/import.py
new file mode 100644
index 0000000..1521849
--- /dev/null
+++ b/misc/server/piztor/import.py
@@ -0,0 +1,44 @@
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from model import *
+
+path = "piztor.sqlite"
+
+class UserData:
+ def __init__(self, username, password, sex):
+ self.username = username
+ self.password = password
+ self.sex = sex
+
+def create_database():
+ engine = create_engine('sqlite:///' + path, echo = True)
+ Base.metadata.drop_all(engine)
+ Base.metadata.create_all(engine)
+
+def import_user_data(data):
+ engine = create_engine('sqlite:///' + path, echo = True)
+ Session = sessionmaker(bind = engine)
+ session = Session()
+ for user in data:
+ um = UserModel(username = user.username, sex = user.sex)
+ um.auth = UserAuth(user.password)
+ um.location = LocationInfo(lat = 0, lng = 0)
+ session.add(um)
+ session.commit()
+
+if __name__ == '__main__':
+
+ from sys import argv, exit
+ if len(argv) != 2:
+ print "Usage: " + argv[0] + " FILE"
+ exit(0)
+
+ data = list()
+ with open(argv[1], 'r') as f:
+ while True:
+ line = f.readline().split()
+ if len(line) == 0: break
+ data.append(UserData(line[0], line[1], line[2]))
+
+ create_database()
+ import_user_data(data)
diff --git a/misc/server/piztor/model.py b/misc/server/piztor/model.py
new file mode 100644
index 0000000..70ca431
--- /dev/null
+++ b/misc/server/piztor/model.py
@@ -0,0 +1,79 @@
+from sqlalchemy import Column, Integer, String, Float, ForeignKey, LargeBinary, Boolean
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import relationship, backref
+
+Base = declarative_base()
+
+_SALT_LEN = 16
+_TOKEN_LEN = 16
+
+class _TableName: # avoid typoes
+ UserModel = 'users'
+ LocationInfo = 'location_info'
+ UserAuth = 'user_auth'
+
+class UserModel(Base):
+ __tablename__ = _TableName.UserModel
+
+ id = Column(Integer, primary_key = True)
+ username = Column(String)
+ sex = Column(Boolean)
+ location = None
+ auth = None
+
+class LocationInfo(Base):
+ __tablename__ = _TableName.LocationInfo
+
+ uid = Column(Integer, ForeignKey('users.id'), primary_key = True)
+ lat = Column(Float(precesion = 64))
+ lng = Column(Float(precesion = 64))
+ user = relationship("UserModel", uselist = False,
+ backref = backref("location", uselist = False,
+ cascade = "all, delete-orphan"))
+
+ # More: last_update
+
+from hashlib import sha256
+from os import urandom
+
+def _sprinkle_salt(uauth, passwd):
+ data = sha256(uauth.salt)
+ data.update(chr(0))
+ data.update(passwd)
+ return data.digest()
+
+def _random_binary_string(length):
+ return urandom(length)
+
+class UserAuth(Base):
+ __tablename__ = _TableName.UserAuth
+
+ uid = Column(Integer, ForeignKey('users.id'), primary_key = True)
+ password = Column(LargeBinary)
+ salt = Column(LargeBinary)
+ token = Column(LargeBinary)
+
+ user = relationship("UserModel", uselist = False,
+ backref = backref("auth", uselist = False,
+ cascade = "all, delete-orphan"))
+
+ def regen_token(self):
+ self.token = sha256(_random_binary_string(_TOKEN_LEN)).digest()
+
+ def __init__(self, passwd):
+ self.set_password(passwd)
+
+ def set_password(self, passwd):
+ self.salt = _random_binary_string(_SALT_LEN)
+ self.password = _sprinkle_salt(self, passwd)
+ self.regen_token()
+
+ def check_password(self, passwd):
+ passwd = _sprinkle_salt(self, passwd)
+ return passwd == self.password
+
+ def check_token(self, tk):
+ return self.token == tk
+
+ def get_token(self):
+ return self.token
diff --git a/misc/server/piztor_server.py b/misc/server/piztor_server.py
new file mode 100644
index 0000000..514f95f
--- /dev/null
+++ b/misc/server/piztor_server.py
@@ -0,0 +1,234 @@
+import sqlalchemy
+import SocketServer, socket, select
+import struct
+import os
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column, Integer, String, Float
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+from random import randint
+
+engine = create_engine('sqlite:///t.sqlite', echo = False)
+Base = declarative_base()
+Session = sessionmaker(bind=engine)
+
+def get_hex(data):
+ return "".join([hex(ord(c))[2:].zfill(2) for c in data])
+
+class PiztorError(Exception):
+ def __init__(self, msg):
+ self.err_msg = msg
+ def __str__(self, msg):
+ return self.err_msg
+
+class ConnectionError(PiztorError):
+ pass
+
+class ReqReadError(ConnectionError):
+ def __init__(self):
+ super(ReqReadError, self).__init__("Error while reading request")
+
+class ReqInvalidError(ConnectionError):
+ def __init__(self):
+ super(ReqInvalidError, self).__init__("Invalid request")
+
+class TokenInvalidError(ConnectionError):
+ def __init__(self):
+ super(TokenInvalidError, self).__init__("Invalid token")
+
+class DataManager(object):
+ def __init__(self, piz_srv):
+ self.piz_srv = piz_srv
+
+class UserManager(DataManager):
+
+ class User(Base):
+ __tablename__ = 'users'
+ id = Column(Integer, primary_key = True)
+ gid = Column(Integer)
+ username = Column(String)
+ password = Column(String)
+ token = Column(Integer)
+
+ def get_user_by_token(self, token):
+ session = Session()
+ User = UserManager.User
+ entries = session.query(User).filter(User.token == token).all()
+ if len(entries) == 0:
+ raise TokenInvalidError()
+ return entries[0]
+
+ def authentication_handle(self, opt_type, data):
+ print "Parsing User Data"
+ pos = -1
+ for i in xrange(0, len(data)):
+ if data[i] == '\0':
+ print i
+ if pos != -1:
+ raise ReqInvalidError()
+ pos = i
+ break
+ if pos == -1:
+ raise ReqInvalidError()
+ username = data[0:pos]
+ password = data[pos + 1:]
+
+ print "Trying to login with following info:"
+ print (username, password)
+
+ session = Session()
+ entries = session.query(UserManager.User). \
+ filter(UserManager.User.username == username).all()
+ if len(entries) == 0:
+ return struct.pack("!BLB", 0, 0, 1)
+ entry = entries[0]
+ if entry.password != password: # Auth failed
+ print "Login failed!"
+ return struct.pack("!BLB", 0, 0, 1)
+ else: # Succeeded
+ print "Logged in sucessfully!"
+ entry.token = randint(0, 2147483647)
+ session.commit()
+ return struct.pack("!BLB", 0, entry.token, 0)
+
+
+class MesgManager(DataManager):
+ def mesg_sending_handle(self, opt_type, data):
+ print "Parsing Mesg Data"
+ try:
+ if len(data) < 8:
+ raise ReqInvalidError()
+ sender_token, recv_id = struct.unpack("!LL", data[:8])
+ msg = data[8:]
+ print (sender_token, recv_id, msg)
+ return struct.pack("!B", 1)
+ except struct.error:
+ raise ReqInvalidError()
+
+class LocationManager(DataManager):
+
+ class LocationInfo(Base):
+ __tablename__ = "location_info"
+ uid = Column(Integer, primary_key = True)
+ lat = Column(Float(precesion = 64))
+ lng = Column(Float(precesion = 64))
+ # More: last_update
+
+ def location_update_handle(self, opt_type, data):
+ print "Parsing a Location Update"
+ try:
+ if len(data) < 8:
+ raise ReqInvalidError()
+ sender_token, lat, lng = struct.unpack("!Ldd", data)
+ print "Updating location data with following info:"
+ print (sender_token, lat, lng)
+
+ user = self.piz_srv. \
+ user_mgr.get_user_by_token(sender_token)
+ session = Session()
+ LInfo = LocationManager.LocationInfo
+ q = session.query(LInfo).filter(LInfo.uid == user.id)
+ entry = q.first()
+ entry.lat = lat
+ entry.lng = lng
+ session.commit()
+ print "Location update succeeded!"
+ return struct.pack("!BB", 2, 0)
+ except TokenInvalidError:
+ print "Location update failed!"
+ return struct.pack("!BB", 2, 1)
+ except struct.error:
+ raise ReqInvalidError()
+
+ def location_request_handle(self, opt_type, data):
+ print "Parsing a Location Request"
+ try:
+ if len(data) != 8:
+ raise ReqInvalidError()
+ sender_token, gid = struct.unpack("!LL", data)
+ print "Requesting location data with following info:"
+ print (sender_token, gid)
+ session = Session()
+ UInfo = UserManager.User
+ LInfo = LocationManager.LocationInfo
+ user_list = session.query(UInfo).filter(UInfo.gid == gid).all()
+ reply = struct.pack("!BL", 3, len(user_list))
+ for user in user_list:
+ loc = session.query(LInfo).filter(LInfo.uid == user.id).first()
+ reply += struct.pack("!Ldd", user.id, loc.lat, loc.lng)
+ print get_hex(reply)
+ return reply
+ except struct.error:
+ raise ReqInvalidError()
+
+class PiztorServer():
+
+
+ class GenericHandler(SocketServer.StreamRequestHandler):
+
+ def handle(self):
+ print self.piz_srv
+ sock = self.request
+ sock.settimeout(100)
+# sock.setblocking(0)
+ data = ""
+ try:
+ while True:
+# ready = select.select([sock], [], [], 10)
+# if not ready[0]:
+# raise ReqReadError()
+ buff = sock.recv(4096)
+ if len(buff) == 0:
+ break # terminated
+ else:
+ data += buff
+ sock.shutdown(socket.SHUT_RD)
+
+ print "Got the data:" + get_hex(data)
+
+ if len(data) < 1:
+ print "invalid length"
+ raise ReqInvalidError()
+ opt_id = struct.unpack("!B", data[0])[0]
+ print opt_id
+ reply = self.piz_srv.mgr_map[opt_id](opt_id, data[1:])
+ sock.sendall(reply)
+ finally:
+ sock.close()
+
+ class ForkingEchoServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
+ pass
+
+ def __init__(self, host, port):
+ PiztorServer.GenericHandler.piz_srv = self
+ srv = PiztorServer.ForkingEchoServer((host, port),
+ PiztorServer.GenericHandler)
+ srv.request_queue_size = 100
+# srv.timeout = 2
+ self.server = srv
+
+ self.user_mgr = UserManager(self)
+ self.mesg_mgr = MesgManager(self)
+ self.location_mgr = LocationManager(self)
+
+ self.mgr_map = [ self.user_mgr.authentication_handle,
+ self.mesg_mgr.mesg_sending_handle,
+ self.location_mgr.location_update_handle,
+ self.location_mgr.location_request_handle]
+
+ Base.metadata.create_all(engine)
+
+
+ def run(self):
+ try:
+ self.server.serve_forever()
+ except KeyboardInterrupt:
+ print "Exiting..."
+ self.server.shutdown()
+ print "Server shutdown"
+
+if __name__ == "__main__":
+
+ ps = PiztorServer("localhost", 9990)
+ ps.run()
diff --git a/misc/server/ptp.rst b/misc/server/ptp.rst
new file mode 100644
index 0000000..8aef2b7
--- /dev/null
+++ b/misc/server/ptp.rst
@@ -0,0 +1,96 @@
+Piztor Transmission Protocol v0.2
+---------------------------------
+
+- General
+
+ - Request
+
+ ::
+
+ +---4b---+---1b---+-------?b--------+
+ | LENGTH | OPT ID | SPECIFIC DATA |
+ +--int---+-uchar--+-----------------+
+
+ - Response
+
+ ::
+
+ +---4b---+---1b---+------?b---------+
+ | LENGTH | OPT ID | SPECIFIC DATA |
+ +--int---+-uchar--+-----------------+
+
+ Notice that in following sections, ``LENGTH`` part is left out for clarity.
+ TODO: All secure requests should have username or uid provided.
+
+- Authentication
+
+ - Request
+
+ ::
+
+ +--1b---+-----?b------+-----?b-----+
+ | 0x00 | USERNAME | PASSWORD |
+ +-uchar-+-------------+------------+
+
+ - Response
+
+ ::
+
+ +--1b---+---1b---+---4b----+----16b-----+
+ | 0x00 | STATUS | USER_ID | USER_TOKEN |
+ +-uchar-+--uchar-+---int---+----raw-----+
+
+ ``STATUS`` :
+
+ - 0x00 for success
+ - 0x01 for failure
+
+- Location Update
+
+ - Request
+
+ ::
+
+ +--1b---+-----16b------+-----8b-----+------8b-----+
+ | 0x02 | USER_TOKEN | LATITUDE | LONGITUDE |
+ +-uchar-+------raw-----+---double---+---double----+
+
+ - Response
+
+ ::
+
+ +--1b---+---1b---+
+ | 0x02 | STATUS |
+ +-uchar-+--uchar-+
+
+ ``STATUS`` :
+
+ - 0x00 for success
+ - 0x01 for invalid token
+
+- Location Information
+
+ - Request
+
+ ::
+
+ +--1b---+-----16b------+------4b-----+
+ | 0x03 | USER_TOKEN | GROUP_ID |
+ +-uchar-+-----raw------+-----int-----+
+
+ - Response
+
+ ::
+
+ +--1b---+---1b---+-----4b----+------20b-------+-----+
+ | 0x03 | STATUS | ENTRY_CNT | LOCATION_ENTRY | ... |
+ +-uchar-+-uchar--+----int----+----------------+-----+
+
+ ``LOCATION_ENTRY`` :
+
+ ::
+
+ +---4b----+----8b----+-----8b----+
+ | USER_ID | LATITUDE | LONGITUDE |
+ +---int---+--double--+--double---+
+
diff --git a/misc/server/rush.py b/misc/server/rush.py
new file mode 100644
index 0000000..f01804c
--- /dev/null
+++ b/misc/server/rush.py
@@ -0,0 +1,14 @@
+from subprocess import Popen
+procs = []
+
+try:
+ for i in xrange(10):
+ p = Popen(["python", "client.py", str(i)])
+ procs.append(p)
+ #p.wait()
+ print "done"
+
+except KeyboardInterrupt:
+ print "killing"
+ for p in procs:
+ p.kill()