summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTeddy <[email protected]>2013-08-25 17:54:38 +0800
committerTeddy <[email protected]>2013-08-25 17:54:38 +0800
commitf74999631c4f83a0c8532d6b7adb348dcd5d5205 (patch)
treec3dcf48d040962c54ec42f87dc10d3a3ef10a508
parenta53f4d21238d2bccd58a22d7485e54f495dbd55d (diff)
ptp v0.3 and std-compliant new server!
-rw-r--r--server/piztor/import.py7
-rw-r--r--server/piztor/model.py5
-rw-r--r--server/piztor/prob.py76
-rw-r--r--server/piztor/server.py311
-rw-r--r--server/piztor_server.py2
-rw-r--r--server/ptp.rst51
6 files changed, 425 insertions, 27 deletions
diff --git a/server/piztor/import.py b/server/piztor/import.py
index 1521849..84c990f 100644
--- a/server/piztor/import.py
+++ b/server/piztor/import.py
@@ -5,9 +5,10 @@ from model import *
path = "piztor.sqlite"
class UserData:
- def __init__(self, username, password, sex):
+ def __init__(self, username, password, gid, sex):
self.username = username
self.password = password
+ self.gid = gid
self.sex = sex
def create_database():
@@ -20,7 +21,7 @@ def import_user_data(data):
Session = sessionmaker(bind = engine)
session = Session()
for user in data:
- um = UserModel(username = user.username, sex = user.sex)
+ um = UserModel(username = user.username, gid = user.gid, sex = user.sex)
um.auth = UserAuth(user.password)
um.location = LocationInfo(lat = 0, lng = 0)
session.add(um)
@@ -38,7 +39,7 @@ if __name__ == '__main__':
while True:
line = f.readline().split()
if len(line) == 0: break
- data.append(UserData(line[0], line[1], line[2]))
+ data.append(UserData(line[0], line[1], line[2], line[3]))
create_database()
import_user_data(data)
diff --git a/server/piztor/model.py b/server/piztor/model.py
index 70ca431..4621bbe 100644
--- a/server/piztor/model.py
+++ b/server/piztor/model.py
@@ -16,6 +16,7 @@ class UserModel(Base):
__tablename__ = _TableName.UserModel
id = Column(Integer, primary_key = True)
+ gid = Column(Integer)
username = Column(String)
sex = Column(Boolean)
location = None
@@ -24,7 +25,7 @@ class UserModel(Base):
class LocationInfo(Base):
__tablename__ = _TableName.LocationInfo
- uid = Column(Integer, ForeignKey('users.id'), primary_key = True)
+ uid = Column(Integer, ForeignKey(_TableName.UserModel + '.id'), primary_key = True)
lat = Column(Float(precesion = 64))
lng = Column(Float(precesion = 64))
user = relationship("UserModel", uselist = False,
@@ -48,7 +49,7 @@ def _random_binary_string(length):
class UserAuth(Base):
__tablename__ = _TableName.UserAuth
- uid = Column(Integer, ForeignKey('users.id'), primary_key = True)
+ uid = Column(Integer, ForeignKey(_TableName.UserModel + '.id'), primary_key = True)
password = Column(LargeBinary)
salt = Column(LargeBinary)
token = Column(LargeBinary)
diff --git a/server/piztor/prob.py b/server/piztor/prob.py
new file mode 100644
index 0000000..1f9bdb7
--- /dev/null
+++ b/server/piztor/prob.py
@@ -0,0 +1,76 @@
+import socket
+from struct import *
+from random import random
+
+def get_hex(data):
+ return "".join([hex(ord(c))[2:].zfill(2) for c in data])
+
+host = "localhost"
+port = 9990
+
+def gen_auth(username, password):
+ length = 4 + 1 + len(username) + 1 + len(password)
+ data = pack("!LB", length, 0x00)
+ data += username
+ data += "\0"
+ data += password
+ return data
+
+def gen_update_location(token, username, lat, lng):
+ length = 4 + 1 + 32 + 8 + 8 + len(username) + 1
+ data = pack("!LB32s", length, 0x01, token)
+ data += username
+ data += chr(0)
+ data += pack("!dd", lat, lng)
+ return data
+
+def gen_request_location(token, username, gid):
+ length = 4 + 1 + 32 + 4 + len(username) + 1
+ data = pack("!LB32s", length, 0x02, token)
+ data += username
+ data += chr(0)
+ data += pack("!L", gid)
+ return data
+
+
+def send(data):
+ received = None
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect((host, port))
+ print len(data)
+ sock.sendall(data)
+ received = sock.recv(1024)
+ finally:
+ sock.close()
+ return received
+
+username = "hello"
+password = "world"
+gid = 1
+
+resp = send(gen_auth(username, password))
+pl, optcode, status, uid, token = unpack("!LBBL32s", resp)
+print "size: " + str((pl, len(resp)))
+print "opt: " + str(optcode)
+print "status: " + str(status)
+print "uid: " + str(uid)
+print "token: " + get_hex(token)
+
+resp = send(gen_update_location(token, username, random(), random()))
+pl, optcode, status = unpack("!LBB", resp)
+print "size: " + str((pl, len(resp)))
+print "opt: " + str(optcode)
+print "status: " + str(status)
+
+resp = send(gen_request_location(token, username, gid))
+print len(resp)
+pl, optcode, status, length = unpack("!LBBL", resp[:10])
+print "size: " + str((pl, len(resp)))
+idx = 10
+print "length: " + str(len(resp[10:]))
+for i in xrange(length):
+ print len(resp[idx:idx + 20])
+ uid, lat, lng = unpack("!Ldd", resp[idx:idx + 20])
+ idx += 20
+ print (uid, lat, lng)
diff --git a/server/piztor/server.py b/server/piztor/server.py
new file mode 100644
index 0000000..5c3160b
--- /dev/null
+++ b/server/piztor/server.py
@@ -0,0 +1,311 @@
+from twisted.internet.protocol import Protocol
+from twisted.internet.protocol import Factory
+from twisted.internet.endpoints import TCP4ServerEndpoint
+from twisted.internet import reactor
+
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
+
+import struct
+import os
+import logging
+
+from exc import *
+from model import *
+
+def get_hex(data):
+ return "".join([hex(ord(c))[2:].zfill(2) for c in data])
+
+def print_datagram(data):
+ print "=================================="
+ print "Received datagram:"
+ print get_hex(data)
+ print "=================================="
+
+db_path = "piztor.sqlite"
+FORMAT = "%(asctime)-15s %(message)s"
+logging.basicConfig(format = FORMAT)
+logger = logging.getLogger('piztor_server')
+logger.setLevel(logging.INFO)
+
+
+class _SectionSize:
+ LENGTH = 4
+ OPT_ID = 1
+ STATUS = 1
+ USER_ID = 4
+ USER_TOKEN = 32
+ GROUP_ID = 4
+ ENTRY_CNT = 4
+ LATITUDE = 8
+ LONGITUDE = 8
+ LOCATION_ENTRY = USER_ID + LATITUDE + LONGITUDE
+ PADDING = 1
+
+class _OptCode:
+ user_auth = 0x00
+ location_update = 0x02
+ location_request= 0x03
+
+class _StatusCode:
+ sucess = 0x00
+ failure = 0x01
+
+class RequestHandler(object):
+ def __init__(self):
+ self.engine = create_engine('sqlite:///' + db_path, echo = False)
+ self.Session = sessionmaker(bind = self.engine)
+
+ @classmethod
+ def get_uauth(cls, token, username, session):
+ try:
+ uauth = session.query(UserAuth) \
+ .filter(UserAuth.token == token).one()
+
+ if uauth.user.username != username:
+ logger.warning("Toke and username mismatch")
+ return None
+
+ return uauth
+
+ except NoResultFound:
+ logger.warning("Incorrect token")
+ return None
+
+ except MultipleResultsFound:
+ raise DBCorruptedError()
+
+ @classmethod
+ def trunc_padding(cls, data):
+ leading = bytes()
+ for i in xrange(len(data)):
+ ch = data[i]
+ if ch == '\x00':
+ print get_hex(leading), get_hex(data[i + 1:])
+ return (leading, data[i + 1:])
+ else:
+ leading += ch
+ # padding not found
+ return (None, data)
+
+class UserAuthHandler(RequestHandler):
+
+ _user_auth_response_size = \
+ _SectionSize.LENGTH + \
+ _SectionSize.OPT_ID + \
+ _SectionSize.STATUS + \
+ _SectionSize.USER_ID + \
+ _SectionSize.USER_TOKEN
+
+ def handle(self, tr_data):
+ logger.info("Reading auth data...")
+ pos = -1
+ for i in xrange(0, len(tr_data)):
+ if tr_data[i] == '\x00':
+ pos = i
+ break
+ if pos == -1:
+ raise BadReqError("Authentication: Malformed request body")
+
+ username = tr_data[0:pos]
+ password = tr_data[pos + 1:]
+ logger.info("Trying to login with " \
+ "(username = {0}, password = {1})" \
+ .format(username, password))
+
+ session = self.Session()
+ try:
+ user = session.query(UserModel) \
+ .filter(UserModel.username == username).one()
+ except NoResultFound:
+ logger.info("No such user: {0}".format(username))
+ return struct.pack("!LBBL32s", UserAuthHandler \
+ ._user_auth_response_size,
+ _OptCode.user_auth,
+ _StatusCode.failure,
+ 0,
+ bytes('\x00' * 32))
+
+ except MultipleResultsFound:
+ raise DBCorruptedError()
+
+ uauth = user.auth
+ if uauth is None:
+ raise DBCorruptedError()
+ if not uauth.check_password(password):
+ logger.info("Incorrect password: {0}".format(username))
+ return struct.pack("!LBBL32s", UserAuthHandler \
+ ._user_auth_response_size,
+ _OptCode.user_auth,
+ _StatusCode.failure,
+ 0,
+ bytes('\x00' * 32))
+ else:
+ logger.info("Logged in sucessfully: {0}".format(username))
+ uauth.regen_token()
+ session.commit()
+ print "new token generated: " + get_hex(uauth.token)
+ return struct.pack("!LBBL32s", UserAuthHandler \
+ ._user_auth_response_size,
+ _OptCode.user_auth,
+ _StatusCode.sucess,
+ user.id,
+ uauth.token)
+
+
+class LocationUpdateHandler(RequestHandler):
+
+# _location_update_size = \
+# _SectionSize.AUTH_HEAD + \
+# _SectionSize.LATITUDE + \
+# _SectionSize.LONGITUDE
+
+ _location_update_response_size = \
+ _SectionSize.LENGTH + \
+ _SectionSize.OPT_ID + \
+ _SectionSize.STATUS
+
+ def handle(self, tr_data):
+ logger.info("Reading location update data...")
+
+ try:
+ token, = struct.unpack("!32s", tr_data[:32])
+ username, tail = RequestHandler.trunc_padding(tr_data[32:])
+ if username is None:
+ raise struct.error
+ lat, lng = struct.unpack("!dd", tail)
+ except struct.error:
+ raise BadReqError("Location update: Malformed request body")
+
+ logger.info("Trying to update location with "
+ "(token = {0}, username = {1}, lat = {2}, lng = {3})"\
+ .format(get_hex(token), username, lat, lng))
+
+ session = self.Session()
+ uauth = RequestHandler.get_uauth(token, username, session)
+ # Authentication failure
+ if uauth is None:
+ logger.warning("Authentication failure")
+ return struct.pack("!LBB", LocationUpdateHandler \
+ ._location_update_response_size,
+ _OptCode.location_update,
+ _StatusCode.failure)
+
+ ulocation = uauth.user.location
+ ulocation.lat = lat
+ ulocation.lng = lng
+ session.commit()
+
+ logger.info("Location is updated sucessfully")
+ return struct.pack("!LBB", LocationUpdateHandler \
+ ._location_update_response_size,
+ _OptCode.location_update,
+ _StatusCode.sucess)
+
+class LocationRequestHandler(RequestHandler):
+
+# _location_request_size = \
+# _SectionSize.AUTH_HEAD + \
+# _SectionSize.GROUP_ID
+
+ @classmethod
+ def _location_request_response_size(cls, item_num):
+ return _SectionSize.LENGTH + \
+ _SectionSize.OPT_ID + \
+ _SectionSize.STATUS + \
+ _SectionSize.ENTRY_CNT + \
+ _SectionSize.LOCATION_ENTRY * item_num
+
+ def handle(self, tr_data):
+ logger.info("Reading location request data..")
+
+ try:
+ token, = struct.unpack("!32s", tr_data[:32])
+ username, tail = RequestHandler.trunc_padding(tr_data[32:])
+ if username is None:
+ raise struct.error
+ gid, = struct.unpack("!L", tail)
+ except struct.error:
+ raise BadReqError("Location request: Malformed request body")
+
+ logger.info("Trying to request locatin with " \
+ "(token = {0}, gid = {1})" \
+ .format(get_hex(token), gid))
+
+ session = self.Session()
+ uauth = RequestHandler.get_uauth(token, username, session)
+ # Auth failure
+ if uauth is None:
+ logger.warning("Authentication failure")
+ return struct.pack("!LBBL", LocationRequestHandler \
+ ._location_request_response_size(0),
+ _OptCode.location_request,
+ _StatusCode.failure,
+ 0)
+
+ ulist = session.query(UserModel).filter(UserModel.gid == gid).all()
+ reply = struct.pack(
+ "!LBBL",
+ LocationRequestHandler._location_request_response_size(len(ulist)),
+ _OptCode.location_request,
+ _StatusCode.sucess,
+ len(ulist))
+
+ for user in ulist:
+ loc = user.location
+ reply += struct.pack("!Ldd", user.id, loc.lat, loc.lng)
+
+ return reply
+
+handlers = [UserAuthHandler,
+ LocationUpdateHandler,
+ LocationRequestHandler]
+
+def check_header(header):
+ return 0 <= header < len(handlers)
+
+class PTP(Protocol):
+
+ def __init__(self):
+ self.buff = bytes()
+ self.length = -1
+
+ def connectionMade(self):
+ logger.info("A new connection is made")
+
+ def dataReceived(self, data):
+ self.buff += data
+ print len(self.buff)
+ if len(self.buff) > 4:
+ try:
+ self.length, self.optcode = struct.unpack("!LB", self.buff[:5])
+ if not check_header(self.optcode): # invalid header
+ raise struct.error
+ except struct.error:
+ logger.warning("Invalid request header")
+ raise BadReqError("Malformed request header")
+ print self.length
+ if len(self.buff) == self.length:
+ h = handlers[self.optcode]()
+ reply = h.handle(self.buff[5:])
+ logger.info("Wrote: %s", get_hex(reply))
+ self.transport.write(reply)
+ self.transport.loseConnection()
+
+ elif len(self.buff) > self.length:
+ self.transport.loseConnection()
+
+
+ def connectionLost(self, reason):
+ logger.info("The connection is lost")
+
+class PTPFactory(Factory):
+ def __init__(self):
+ pass
+ def buildProtocol(self, addr):
+ return PTP()
+
+endpoint = TCP4ServerEndpoint(reactor, 9990)
+endpoint.listen(PTPFactory())
+reactor.run()
diff --git a/server/piztor_server.py b/server/piztor_server.py
index 514f95f..81805b3 100644
--- a/server/piztor_server.py
+++ b/server/piztor_server.py
@@ -230,5 +230,5 @@ class PiztorServer():
if __name__ == "__main__":
- ps = PiztorServer("localhost", 9990)
+ ps = PiztorServer("localhost", 9999)
ps.run()
diff --git a/server/ptp.rst b/server/ptp.rst
index 8aef2b7..7c40a3b 100644
--- a/server/ptp.rst
+++ b/server/ptp.rst
@@ -1,4 +1,4 @@
-Piztor Transmission Protocol v0.2
+Piztor Transmission Protocol v0.3
---------------------------------
- General
@@ -8,7 +8,7 @@ Piztor Transmission Protocol v0.2
::
+---4b---+---1b---+-------?b--------+
- | LENGTH | OPT ID | SPECIFIC DATA |
+ | LENGTH | OPT_ID | SPECIFIC DATA |
+--int---+-uchar--+-----------------+
- Response
@@ -16,11 +16,20 @@ Piztor Transmission Protocol v0.2
::
+---4b---+---1b---+------?b---------+
- | LENGTH | OPT ID | SPECIFIC DATA |
+ | 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.
+ Notice:
+
+ - In following sections, ``LENGTH`` part is left out for clarity.
+ - ``PADDING`` has value ``0``.
+ - ``AUTH_HEAD`` structure:
+
+ ::
+
+ +----32b-----+----?b----+----1b---+
+ | USER_TOKEN | USERNAME | PADDING |
+ +----raw-----+----------+---------+
- Authentication
@@ -28,22 +37,22 @@ Piztor Transmission Protocol v0.2
::
- +--1b---+-----?b------+-----?b-----+
- | 0x00 | USERNAME | PASSWORD |
- +-uchar-+-------------+------------+
+ +--1b---+-----?b------+----1b----+-----?b-----+
+ | 0x00 | USERNAME | PADDING | PASSWORD |
+ +-uchar-+-------------+----------+------------+
- Response
::
- +--1b---+---1b---+---4b----+----16b-----+
+ +--1b---+---1b---+---4b----+----32b-----+
| 0x00 | STATUS | USER_ID | USER_TOKEN |
+-uchar-+--uchar-+---int---+----raw-----+
``STATUS`` :
- - 0x00 for success
- - 0x01 for failure
+ - ``0x00`` for success
+ - ``0x01`` for failure
- Location Update
@@ -51,22 +60,22 @@ Piztor Transmission Protocol v0.2
::
- +--1b---+-----16b------+-----8b-----+------8b-----+
- | 0x02 | USER_TOKEN | LATITUDE | LONGITUDE |
- +-uchar-+------raw-----+---double---+---double----+
+ +--1b---+-----?b------+----8b------+------8b-----+
+ | 0x01 | AUTH_HEAD | LATITUDE | LONGITUDE |
+ +-uchar-+-------------+---double---+---double----+
- Response
::
+--1b---+---1b---+
- | 0x02 | STATUS |
+ | 0x01 | STATUS |
+-uchar-+--uchar-+
``STATUS`` :
- - 0x00 for success
- - 0x01 for invalid token
+ - ``0x00`` for success
+ - ``0x01`` for invalid token
- Location Information
@@ -74,16 +83,16 @@ Piztor Transmission Protocol v0.2
::
- +--1b---+-----16b------+------4b-----+
- | 0x03 | USER_TOKEN | GROUP_ID |
- +-uchar-+-----raw------+-----int-----+
+ +--1b---+------?b------+------4b-----+
+ | 0x02 | AUTH_HEAD | GROUP_ID |
+ +-uchar-+--------------+-----int-----+
- Response
::
+--1b---+---1b---+-----4b----+------20b-------+-----+
- | 0x03 | STATUS | ENTRY_CNT | LOCATION_ENTRY | ... |
+ | 0x02 | STATUS | ENTRY_CNT | LOCATION_ENTRY | ... |
+-uchar-+-uchar--+----int----+----------------+-----+
``LOCATION_ENTRY`` :