This commit is contained in:
louiscklaw
2025-01-31 19:51:47 +08:00
parent 118e4a5f39
commit 866bfd3b42
189 changed files with 43536 additions and 0 deletions

0
hk1234566/.env.docker Normal file
View File

View File

View File

@@ -0,0 +1,8 @@
BattleDate,BattleNo,Attacker,Defender,Weapon,Result,Damage
15-May-23,5481,Calvin,SneakySue,Axe,Hit,87
15-May-23,5481,SneakySue,Calvin,Dagger,Miss,0
15-May-23,5481,Calvin,SneakySue,Axe,Hit,94
15-May-23,5481,SneakySue,Calvin,Dagger,Parry,0
15-May-23,5481,Calvin,SneakySue,Axe ,Hit,78
15-May-23,5481,SneakySue,Calvin,Dagger,Hit,14
15-May-23,5481,Calvin,SneakySue,Axe,Victory,112
1 BattleDate BattleNo Attacker Defender Weapon Result Damage
2 15-May-23 5481 Calvin SneakySue Axe Hit 87
3 15-May-23 5481 SneakySue Calvin Dagger Miss 0
4 15-May-23 5481 Calvin SneakySue Axe Hit 94
5 15-May-23 5481 SneakySue Calvin Dagger Parry 0
6 15-May-23 5481 Calvin SneakySue Axe Hit 78
7 15-May-23 5481 SneakySue Calvin Dagger Hit 14
8 15-May-23 5481 Calvin SneakySue Axe Victory 112

View File

@@ -0,0 +1,8 @@
Account_Number,Forename,surname,e-mail_Address,Character_CreationDate,Character_Expiry_Date,Character_Name,Character_Type,Level,ExperiencePoints,Max_Health,Health,AttackinScore,DefenceScore,StealthScore,ManaScore,Money_bank,Money_wallet
15468336,Jones,Susan,sj56@gmail.com,02-Jan-2022,,juju97,Warrior,12,12475,1000,996,85,23,17,,4273.95,351
15468336,Jones,Susan,sj56@gmail.com,15-Mar-2022,,SneakySue,Thief,7,7201,900,856,37,12,56,,11730.34,295
15468336,Jones,Susan,sj56@gmail.com,14-Jul-2022,19-Aug-2022,DarkMage,Mage,3,2012,250,250,12,17,9,21,0,0
35482956,Turker,Uraz,u.turker@lancaster.ac.uk,05-Mar-2022,,Uraz-The-Magnificent,Mage,34,34925,1200,1094,34,49,21,64,11397,700
54387937,Schmidt,Johan,jjs46@hotmail.com,08-Jan2023,,SpacemanSpiff,Mage,3,1085,250,227,9,19,8,31,548,102
64345236,Ricki,Boswell-Challand,r.boswell-challand@lancaster.ac.uk,24-Apr-2022,,Calvin,Merchant,18,21765,950,950,54,53,36,,34950,3705
64345236,Ricki,Boswell-Challand,r.boswell-challand@lancaster.ac.uk,13-May2022,,Hobbes,Warrior,95,95678,1500,1500,88,79,84,,0,0
1 Account_Number Forename surname e-mail_Address Character_CreationDate Character_Expiry_Date Character_Name Character_Type Level ExperiencePoints Max_Health Health AttackinScore DefenceScore StealthScore ManaScore Money_bank Money_wallet
2 15468336 Jones Susan sj56@gmail.com 02-Jan-2022 juju97 Warrior 12 12475 1000 996 85 23 17 4273.95 351
3 15468336 Jones Susan sj56@gmail.com 15-Mar-2022 SneakySue Thief 7 7201 900 856 37 12 56 11730.34 295
4 15468336 Jones Susan sj56@gmail.com 14-Jul-2022 19-Aug-2022 DarkMage Mage 3 2012 250 250 12 17 9 21 0 0
5 35482956 Turker Uraz u.turker@lancaster.ac.uk 05-Mar-2022 Uraz-The-Magnificent Mage 34 34925 1200 1094 34 49 21 64 11397 700
6 54387937 Schmidt Johan jjs46@hotmail.com 08-Jan2023 SpacemanSpiff Mage 3 1085 250 227 9 19 8 31 548 102
7 64345236 Ricki Boswell-Challand r.boswell-challand@lancaster.ac.uk 24-Apr-2022 Calvin Merchant 18 21765 950 950 54 53 36 34950 3705
8 64345236 Ricki Boswell-Challand r.boswell-challand@lancaster.ac.uk 13-May2022 Hobbes Warrior 95 95678 1500 1500 88 79 84 0 0

View File

@@ -0,0 +1,230 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true
});
</script>
</head>
<body>
<div class="mermaid">
---
title: Order example
---
erDiagram
CUSTOMER |o--o{ Warrior : Own
CUSTOMER |o--o{ Thief : Own
CUSTOMER |o--o{ Mage : Own
CUSTOMER |o--o{ Merchant : Own
ITEMS |o--o{ Warrior : Own
ITEMS |o--o{ Thief : Own
ITEMS |o--o{ Mage : Own
ITEMS |o--o{ Merchant : Own
Warrior }o--o{ COMBAT : Attack
Thief }o--o{ COMBAT : Attack
Mage }o--o{ COMBAT : Attack
Merchant }o--o{ COMBAT : Attack
Warrior }o--o{ COMBAT : Defense
Thief }o--o{ COMBAT : Defense
Mage }o--o{ COMBAT : Defense
Merchant }o--o{ COMBAT : Defense
COMBAT }o--o{ BATTLE : Own
CUSTOMER {
string AccountNumber PK
string e-mail_Address
string Forename
string surname
}
Merchant {
int AccountNumber FK
string CharacterCreationDate
string CharacterExpiryDate
string CharacterName
string CharacterType
string Level
string ExperiencePoints
string MaxHealth
string Health
string AttackinScore
string DefenceScore
string StealthScore
string ManaScore
string MoneyBank
string MoneyWallet
}
Mage {
int AccountNumber FK
string CharacterCreationDate
string CharacterExpiryDate
string CharacterName
string CharacterType
string Level
string ExperiencePoints
string MaxHealth
string Health
string AttackinScore
string DefenceScore
string StealthScore
string ManaScore
string MoneyBank
string MoneyWallet
}
Thief {
int AccountNumber FK
string CharacterCreationDate
string CharacterExpiryDate
string CharacterName
string CharacterType
string Level
string ExperiencePoints
string MaxHealth
string Health
string AttackinScore
string DefenceScore
string StealthScore
string ManaScore
string MoneyBank
string MoneyWallet
}
Warrior {
int AccountNumber FK
string CharacterCreationDate
string CharacterExpiryDate
string CharacterName
string CharacterType
string Level
string ExperiencePoints
string MaxHealth
string Health
string AttackinScore
string DefenceScore
string StealthScore
string ManaScore
string MoneyBank
string MoneyWallet
}
Score {
int characterKey FK
string ManaScore
string StealthScore
string DefenceScore
string AttackInScore
}
COMBAT {
int AccountNumber_CharacterName FK
string BattleDate
string BattleNo
string Attacker
string Defender
string Weapon
string Result
string Damage
}
ITEMS {
string Character
string Item
string Item_Type
string WeaponType
string Range
string Price
string Quantity
string DefendScore
string AttackScore
string HealingScore
string ManaScore
string SingleUse
string wearable
string worn
string BodyPart
string Equipped
}
ARMOUR {
string Price
string Quantity
string DefendScore
string SingleUse
string wearable
string worn
string BodyPart
string Equipped
}
ARTEFACT {
string Price
string Quantity
string DefendScore
string AttackScore
string ManaScore
string SingleUse
string wearable
string Equipped
}
CLOTHING {
string Price
string Quantity
string DefendScore
string ManaScore
string SingleUse
string wearable
string worn
string BodyPart
string Equipped
}
FOOD {
string Price
string Quantity
string HealingScore
string SingleUse
string wearable
string worn
string Equipped
}
WEAPON {
string WeaponType
string Range
string Price
string Quantity
string DefendScore
string AttackScore
string HealingScore
string SingleUse
string wearable
string Equipped
}
BATTLE {
string BattleDate
string BattleNo
}
COMBAT {
string Attacker FK
string Defender FK
string Weapon
string Result
string Damage
}
</div>
</body>
</html>

View File

@@ -0,0 +1,26 @@
@echo off
setlocal
if [%2] equ [] (
echo dot2 ext dotfile
exit /b
)
set extension=%1
set dotFile=%2
set dotFile_=%~n2%
if not exist %dotFile% (
echo %dotFile% does not exist
exit /b
)
dot -T%extension% -o%dotFile_%.%extension% %dotFile%
if %errorlevel% neq 0 (
echo ErrorLevel: %errorlevel%
exit /b
)
@REM start %dotFile_%.%extension%

View File

@@ -0,0 +1,63 @@
graph ER {
size="15,15"
fontname="Helvetica,Arial,sans-serif"
node [fontname="Helvetica,Arial,sans-serif", fontsize=10;]
edge [fontname="Helvetica,Arial,sans-serif"]
layout=neato
node [shape=triangle,color=red]; "ISA";
node [shape=box, color=black]; course; institute; student; Customers;
node [shape=box, color=black]; Customers;
node [shape=ellipse]; AccountNumber; EMailAddress; Name; Forename; Surname;
node [shape=ellipse]; {node [label="Name"] name0; name1; name2;}
Customers -- AccountNumber [len=2.0];
Customers -- EMailAddress [len=1.0];
Customers -- name0 [len=1.0];
Name -- Forename [len=1.5];
Name -- Surname [len=1.0];
node [shape=box,color=black, peripheries=2]; "Characters";
node [shape=ellipse, peripheries=1]; AccountNumber; Forename; surname; EMailAddress; CharacterCreationDate; CharacterExpiryDate; CharacterName; CharacterType; Level; ExperiencePoints; MaxHealth; Health; AttackinScore; DefenceScore; StealthScore; ManaScore; Moneybank; Moneywallet;
Characters -- Level [len=2.0];
Characters -- Type [len=2.0];
Characters -- Name [len=2.0];
Characters -- MaxHealth [len=2.0];
Characters -- StealthScore [len=2.0];
Characters -- ExperiencePoints [len=2.0];
Characters -- DefenceScore [len=2.0];
Characters -- MoneyBank [len=2.0];
Characters -- AttackInScore [len=2.0];
Characters -- MoneyWallet [len=2.0];
Characters -- Health [len=2.0];
Characters -- ManaScore [len=2.0];
Characters -- Date [len=2.0];
Date -- Creation [len=2.0];
Date -- Expiry [len=2.0];
Date -- BillDuration [len=2.0];
node [shape=ellipse]; {node [label="name"] name0; name1; name2;} code; grade; number;
node [shape=diamond,color=black, peripheries=2]; "C-I"; "S-C"; "S-I";
node [shape=box,color=black, peripheries=2]; "RECT2";
node [shape=ellipse,color=black, peripheries=2]; "ellipse2";
RECT2 -- ellipse2 [label="n",len=3.00, color="black:white:black"];
name0 -- course;
code -- course;
course -- "C-I" [label="n",len=1.00];
"C-I" -- institute [label="1",len=1.00];
institute -- name1;
institute -- "S-I" [label="1",len=1.00];
"S-I" -- student [label="n",len=1.00];
student -- grade;
student -- name2;
student -- number;
student -- "S-C" [label="m",len=1.00];
"S-C" -- course [label="n",len=1.00];
label = "\n\nEntity Relation Diagram\ndrawn by NEATO";
fontsize=10;
}

View File

@@ -0,0 +1,27 @@
graph ER {
fontname="Helvetica,Arial,sans-serif"
node [fontname="Helvetica,Arial,sans-serif", fontsize=10]
edge [fontname="Helvetica,Arial,sans-serif"]
layout=neato
node [shape=box]; course; institute; student;
node [shape=ellipse]; {node [label="name"] name0; name1; name2;}
code; grade; number;
node [shape=diamond,style=filled,color=lightgrey]; "C-I"; "S-C"; "S-I";
name0 -- course;
code -- course;
course -- "C-I" [label="n",len=1.00];
"C-I" -- institute [label="1",len=1.00];
institute -- name1;
institute -- "S-I" [label="1",len=1.00];
"S-I" -- student [label="n",len=1.00];
student -- grade;
student -- name2;
student -- number;
student -- "S-C" [label="m",len=1.00];
"S-C" -- course [label="n",len=1.00];
label = "\n\nEntity Relation Diagram\ndrawn by NEATO";
fontsize=20;
}

BIN
hk1234566/SQL/Assessment1/ER/helloworld.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="helloworld.png" />
</body>
</html>

View File

@@ -0,0 +1,15 @@
Character,Item,Item_Type,WeaponType,Range,Price,Quantity,DefendScore,AttackScore,HealingScore,ManaScore,SingleUse,wearable,worn,BodyPart,Equipped
juju97,breastplate,Armour,,,290,1,18,,,,0,1,1,torso,0
Uraz-The-Magnificent,Wizard's Staff,Artefact,,,3500,1,0,0,,37,0,,,,1
SpacemanSpiff,Transmogrifier,Artefact,,,54000,1,,,,500,0,0,,,1
Uraz-The-Magnificent,Wizard's Robe,Clothing,,,2000,1,10,,,,,1,1,torso,0
juju97,Tunic,Clothing,,,5,1,1,0,,,0,1,1,torso,
juju97,Cloak of Invisibility,Clothing,,,4500,1,,,,75,,1,0,torso,
Uraz-The-Magnificent,Apple,Food,,,1,1,,,10,,1,,,,0
juju97,Bread,Food,,,0.75,3,,,8,,1,,,,0
SpacemanSpiff,Can of Tuna,Food,,,3,4,,,15,,1,0,,,1
Uraz-The-Magnificent,Cheese,Food,,,6,10,,,12,,1,0,0,,0
juju97,Broadsword,Weapon,Melee,,175,1,,24,,,0,0,,,1
juju97,slingshot,Weapon,Projectile,30,50,1,,13,,,0,0,,,0
SneakySue,Dagger,Weapon,Melee,,12,1,1,10,0,,0,0,,,1
Calvin,Axe,Weapon,Melee,,80,1,,,,,,,,,1
1 Character Item Item_Type WeaponType Range Price Quantity DefendScore AttackScore HealingScore ManaScore SingleUse wearable worn BodyPart Equipped
2 juju97 breastplate Armour 290 1 18 0 1 1 torso 0
3 Uraz-The-Magnificent Wizard's Staff Artefact 3500 1 0 0 37 0 1
4 SpacemanSpiff Transmogrifier Artefact 54000 1 500 0 0 1
5 Uraz-The-Magnificent Wizard's Robe Clothing 2000 1 10 1 1 torso 0
6 juju97 Tunic Clothing 5 1 1 0 0 1 1 torso
7 juju97 Cloak of Invisibility Clothing 4500 1 75 1 0 torso
8 Uraz-The-Magnificent Apple Food 1 1 10 1 0
9 juju97 Bread Food 0.75 3 8 1 0
10 SpacemanSpiff Can of Tuna Food 3 4 15 1 0 1
11 Uraz-The-Magnificent Cheese Food 6 10 12 1 0 0 0
12 juju97 Broadsword Weapon Melee 175 1 24 0 0 1
13 juju97 slingshot Weapon Projectile 30 50 1 13 0 0 0
14 SneakySue Dagger Weapon Melee 12 1 1 10 0 0 0 1
15 Calvin Axe Weapon Melee 80 1 1

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
hk1234566/SQL/Assessment1/docs/ER_for_characters.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/docs/ER_for_combat.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/docs/ER_for_customers.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/docs/ER_for_items.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/docs/ER_overview.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,92 @@
# ER and attributes for combat
![](./ER_for_combat.png)
### Entity
A Battle has a Battle Number (the primary key) and Date(BattleDate) attribute.
A Combat has a Damage, Weapon and a Result attribute.
### Relationship: Involved (battle)
A combat must have battle involved.
A battle will contain at least on combat.
### Relationship: Involved (Attacker / Defender)
A combat must have at least 1 characters involved.
A Characters may contain empty combat history
# ER and attributes for characters
![](./ER_for_characters.png)
### Entity
A Characters has a Name(key), Type, Level, ExperiencePoints, MaxHealth, Health, AttackInScore, DefenceScore, StealthScore, ManaScore, MoneyBank MoneyWallet attribute.
### Relationship: OwnedBy
A character must owned by one player
A player may have many characters
### Relationship: OwnItems
An Items must owned by one character
A character may have many Items
If a character is deleted from the game server, the items will be deleted
### Relationship: Involved
A Combat (either attacker/defender) must owned by one character
A character may involved in many combats
If a character is deleted from the game server, the combat will be deleted
# ER and attributes for customers/players
![](./ER_for_customers.png)
## Entity set
A customers has a AccountNumber(key), Name and EMailAddress attribute.
Name is a Multi-valued attribute containing Forename and Surname.
## Relationship Owned By
A Characters must owned by one character
A customers/player may have many character
If a customer/player is deleted from the game server, the characters will be deleted
# ER and attributes for items/inventory
![](./ER_for_items.png)
## Entity set
A Items has a ItemName, ItemType, WeaponType, Quantity, Range, SingleUse, Price, Equipped, BodyPart, Wearable, Worn attribute
A Score is a Multi-valued attribute containing ManaScore, DefendScore and AttackScore.
A ItemKey is an key to indicate the specific item
## Relationship OwnedItems
A Items must owned by one character
A character may have many items
If a character is deleted from the game server, the Items will be deleted
# Overview of ERs
![](./ER_overview.png)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,85 @@
Players(customers)
- requirements:
- A player must have at least one character (Characters_owned)
- can create a new character or
- deactivate one they no longer wish to play
- Once a player unsubscribed from the game, the character is also removed from the system.
- A player will be billed at the end of a calendar month
- for all active characters for any part of that month.
- fields:
- Account_Number(pk)
- Forename
- surname
- e-mail_Address
- Characters_owned(1 to many to characters)
Characters
- requirements:
- Characters have combat info (see Combat.csv)
- fields:
- Name(Character_Name)
- Type(Character_Type)
- Level
- ExperiencePoints
- Max_Health
- Health
- AttackInScore
- DefenceScore
- StealthScore
- ManaScore
- MoneyBank
- MoneyWallet
- OwnedBy (many to 1 to players)
- Items (1 to Many to items table)
- CombatInvolved (1 to many to Combat table / Battle)
- CreationDate(Character_CreationDate)
- ExpiryDate(Character_Expiry_Date)
Inventory(Items)
- for the various weapons and armour a character may possess
- Weapons:
- range
- damage points.
- Armours:
- defence score
- worn on a speciÞc body part.
- Supplies:
- healing score,
- mana score
- if consumed increase the characters life score by that amount (after which time the character will possess one fewer of that type of item)
- fields:
- ItemsKey(pk)
- Item_Name(Item)
- Item_Type
- WeaponType
- Range
- Price
- Quantity
- DefendScore
- AttackScore
- HealingScore
- ManaScore
- SingleUse
- wearable
- worn
- BodyPart
- Equipped
- Character (many to 1 to character / Items)
Combat:
- requirements:
- \# of kills
- KIAs(Killed in action)
- victories
- fields:
- BattleNo(pk)
- BattleDate
- Attacker(many to 1 -> characters)
- Defender(many to 1 -> characters)
- Weapon
- Result
- Damage

View File

@@ -0,0 +1,29 @@
# notes.md
![](./combat.png)
Your ERD should handle the following rules as a minimum but please use your
imagination to provide further rules:
a) A player must have at least one character and can create a new character or
deactivate one they no longer wish to play at any time. Once a player
unsubscribed from the game, the character is also removed from the system.
b) A player will be billed at the end of a calendar month for all active characters
for any part of that month.
c) A character can possess any number of inventory items (see Inventories.csv):
a. Weapons: have a range, damage points.
b. Armours: has a defence score and is worn on a speci c body part.
c. Supplies: have a healing score, mana score and if consumed increase the characters life score by that amount (after which time the character will possess one fewer of that type of item)
d) Characters have combat info (see Combat.csv)
1. Your initial E-R diagram (WEEK 8 30%)
You will provide two outputs:
1) An English paragraph given in a PDF Þle that describes your personal ERD and
2) your ERD (embedded in the PDF Þle). We will assess your ERD by reading your paragraph.
You will receive full mark if your paragraph matches to your ERD.
Otherwise we will reduce your marks based on the following rules:
a) For each participation constraint fault (-1.5%).
b) For each multiplicity fault (-1.5%).
c) For each notation/Symbol fault (-1.5%).

View File

@@ -0,0 +1,19 @@
In the followings we will provide you some requirements,
however we will expect you to exceed these expectations. You will need to demonstrate these in three milestones:
MILESTONES
1) Design and develop an Entity-Relationship Diagram (ERD) that models the following
key entities:
a) Customers/Players.
b) Characters.
c) Inventory for the various weapons and armour a character may possess.
d) Combat activities (#of kills, KIAs, victories, etc.).
Your ERD should handle the following rules as a minimum but please use your imagination to provide further rules:
a) A player must have at least one character and can create a new character or deactivate one they no longer wish to play at any time. Once a player unsubscribed from the game, the character is also removed from the system.
b) A player will be billed at the end of a calendar month for all active characters for any part of that month.
c) A character can possess any number of inventory items (see Inventories.csv):
a. Weapons: have a range, damage points.
b. Armours: has a defence score and is worn on a speciÞc body part.
c. Supplies: have a healing score, mana score and if consumed increase the characters life score by that amount (after which time the character will possess one fewer of that type of item)
d) Characters have combat info (see Combat.csv)

View File

@@ -0,0 +1 @@
digraph G {Hello->World}

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script charset="utf-8" type="text/javascript" src="live.bc.js" ></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q=" crossorigin="anonymous" />
<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.js" integrity="sha256-qs5p0BFSqSvrstBxPvex+zdyrzcyGdHNeNmAirO2zc0=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tonsky/FiraCode@1.207/distr/fira_code.css">
<link rel="stylesheet" href="style.css" />
<title>ovenMPST IDE</title>
<script src="https://cdn.jsdelivr.net/npm/iblize/dist/iblize.min.js"></script>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/viz.js" integrity="sha256-8RHyK+AFzq9iXwbFo2unqidwPbwHU5FFWe3RwkcVtuU=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/viz.js/2.1.2/full.render.js" integrity="sha256-Ogqs510LFnekr9o7OLdpelaaAmNss9egQRTyzCqV2NQ=" crossorigin="anonymous"></script>
<script>
function getProtocolName() {
var ele = document.getElementsByName('protocol_choice');
for(i = 0; i < ele.length; i++) {
if(ele[i].checked) return ele[i].value.trim();
}
return "";
}
function getOutputFormat() {
var ele = document.getElementById('output_format');
var ret = ele.value;
return ret;
}
</script>
<div class="ui fixed menu">
<div class="ui container">
<a href="https://nuscr.dev/oven/" class="header item">
<img class="logo" src="ovenMPST.png">
OvenMPST
</a>
<!-- <a href="docs" class="item">Documentation</a> -->
<a href="https://github.com/nuscr/oven" target="_blank" rel="noopener noreferrer" class="item">GitHub</a>
</div>
</div>
<div class="ui main container">
<h1 class="ui header">
<img src="ovenMPST.png" alt="Logo" />
OvenMPST IDE
</h1>
<div class="ui grid">
<div class="eight wide column">
<h2>Global protocol</h2>
<div rows="30" id="protocol-textarea" default="Enter text..."></div>
<script>
// use selector
const iblize = new Iblize("#protocol-textarea", {
language: "html",
lineNumber: true,
theme: "iblize-light",
// etc
});
to1 = window.setTimeout(() => console.log("empty timer."), 1);
to2 = window.setTimeout(() => console.log("empty timer."), 1);
function clear_and_parse () {
ovenInterface.clear(1); ovenInterface.parse(1);
}
iblize.onUpdate((value) => {
window.clearTimeout(to1);
to1 = window.setTimeout(clear_and_parse, 100);
window.clearTimeout(to2);
to2 = window.setTimeout(()=> ovenInterface.render(1), 1000);
});
</script>
<br />
<select required class="ui dropdown" id="examples">
<option value="" disabled selected hidden>Load an example</option>
</select>
<select required class="ui dropdown" id="output_format">
<option value="graph" selected>Show graph</option>
<option value="dot">Show graphviz dot file</option>
</select>
<button class="ui button" id="button">Analyse</button>
</div>
<div class="eight wide column" id="output">
</div>
<div class="ui negative message" id="errorbox" hidden></div>
<div class="sixteen wide column">
<h2> Global Machine </h2>
<!-- remove the unused ones -->
<div id="result"></div>
<pre id="projected"/></pre>
<div id="efsm" style="overflow: scroll;"></div>
</div>
<div class = "sixteen wide column">
<h2>Local Projections </h2>
<div id="local">
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,519 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2023-03-09T04:41:02.806Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36" etag="qIToQRhJueHNQSkVaT_3" version="20.8.5" type="device">
<diagram id="gbh2_RjxMlchc6C8Ck6F" name="Page-2">
<mxGraphModel dx="1052" dy="1885" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="W42QqZGia2SB_BN2uIFJ-129" value="N" style="endArrow=none;html=1;rounded=0;strokeWidth=2;fontSize=14;entryX=0.025;entryY=0.499;entryDx=0;entryDy=0;shape=link;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-19" target="W42QqZGia2SB_BN2uIFJ-8" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="584" y="-546.75" as="sourcePoint" />
<mxPoint x="639" y="-546.75" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-1" value="Customers" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;direction=west;" parent="1" vertex="1">
<mxGeometry x="746.5" y="-937" width="135" height="120" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-2" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="774" y="-722" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-3" value="" style="rhombus;whiteSpace=wrap;html=1;" parent="W42QqZGia2SB_BN2uIFJ-2" vertex="1">
<mxGeometry width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-4" value="Owned&lt;br style=&quot;border-color: var(--border-color); padding: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;&quot;&gt;By&lt;/span&gt;" style="rhombus;whiteSpace=wrap;html=1;" parent="W42QqZGia2SB_BN2uIFJ-2" vertex="1">
<mxGeometry x="10" y="10" width="60" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-5" value="N" style="endArrow=none;html=1;rounded=0;exitX=0.505;exitY=1.027;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;strokeWidth=2;shape=link;fontSize=14;exitPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-3" target="W42QqZGia2SB_BN2uIFJ-15" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1049" y="-596.25" as="sourcePoint" />
<mxPoint x="1099" y="-646.25" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-7" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="594" y="-590" width="115" height="83.75" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-8" value="" style="rhombus;whiteSpace=wrap;html=1;" parent="W42QqZGia2SB_BN2uIFJ-7" vertex="1">
<mxGeometry width="115" height="83.75" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-9" value="Own&lt;br&gt;Items" style="rhombus;whiteSpace=wrap;html=1;" parent="W42QqZGia2SB_BN2uIFJ-7" vertex="1">
<mxGeometry x="14.375" y="10.46875" width="86.25" height="62.8125" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-11" value="Involved&lt;br&gt;(battle)" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="989" y="-346.25" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-12" value="M" style="endArrow=none;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=2;fontSize=14;shape=link;" parent="1" source="W42QqZGia2SB_BN2uIFJ-11" target="W42QqZGia2SB_BN2uIFJ-22" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="899" y="-58.379999999999995" as="sourcePoint" />
<mxPoint x="867.5" y="-30.129999999999995" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-13" value="N" style="endArrow=none;html=1;rounded=0;strokeWidth=2;fontSize=14;entryX=0;entryY=0.5;entryDx=0;entryDy=0;shape=link;" parent="1" target="W42QqZGia2SB_BN2uIFJ-11" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="934" y="-306.25" as="sourcePoint" />
<mxPoint x="897.5" y="-94.13" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-14" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="764" y="-581.25" width="100" height="70" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-15" value="Characters" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-14" vertex="1">
<mxGeometry width="100" height="70" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-16" value="Characters" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-14" vertex="1">
<mxGeometry x="10" y="10" width="80" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-17" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="414" y="-572.65" width="88.72" height="52.8" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-18" value="Items" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-17" vertex="1">
<mxGeometry width="88.72" height="52.79999999999999" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-19" value="Items" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-17" vertex="1">
<mxGeometry x="6.571851851851852" y="6.700507614213197" width="76.41749333333334" height="39.398984771573595" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-20" value="1" style="endArrow=none;html=1;rounded=0;entryX=0;entryY=0.471;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=2;fontSize=14;entryPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-8" target="W42QqZGia2SB_BN2uIFJ-15" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="624" y="-456.25" as="sourcePoint" />
<mxPoint x="674" y="-506.25" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-21" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="1124" y="-329.43999999999994" width="92.5" height="47.129999999999995" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-22" value="Battle" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-21" vertex="1">
<mxGeometry width="92.5" height="47.13" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-23" value="Battle" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-21" vertex="1">
<mxGeometry x="8.75" y="6.059999999999945" width="75" height="35.75" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-24" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="844" y="-336.25" width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-25" value="Combat" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-24" vertex="1">
<mxGeometry width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-26" value="Combat" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=2;fontSize=14;" parent="W42QqZGia2SB_BN2uIFJ-24" vertex="1">
<mxGeometry x="7.5" y="12.120000000000005" width="75" height="36.5" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-27" value="Involved&lt;br&gt;(battle)" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="994" y="-341.25" width="70" height="70" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-28" value="BattleDate" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="1261.5" y="-298.37" width="78.75" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-29" value="" style="endArrow=none;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=2;fontSize=14;entryX=0.017;entryY=0.591;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="W42QqZGia2SB_BN2uIFJ-28" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1216.5" y="-300.3699999999999" as="sourcePoint" />
<mxPoint x="1311.505" y="-304.22575000000006" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-30" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=2;fontSize=14;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1.004;exitY=0.394;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" target="W42QqZGia2SB_BN2uIFJ-113" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1216.5" y="-304.3699999999999" as="sourcePoint" />
<mxPoint x="1278.7800000000002" y="-309.4399999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-31" value="Weapon" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="894" y="-207" width="75" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-32" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=3;entryX=0.395;entryY=1.023;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-31" target="W42QqZGia2SB_BN2uIFJ-25" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="437" y="-53" as="sourcePoint" />
<mxPoint x="623" y="-262" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-33" value="Result" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="799" y="-207" width="75" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-34" value="" style="endArrow=none;html=1;rounded=0;exitX=0.63;exitY=0.051;exitDx=0;exitDy=0;strokeWidth=3;exitPerimeter=0;entryX=0.13;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-33" target="W42QqZGia2SB_BN2uIFJ-25" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="757" y="-271.25" as="sourcePoint" />
<mxPoint x="854" y="-278" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-35" value="Damage" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="989" y="-207" width="75" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-36" value="" style="endArrow=none;html=1;rounded=0;exitX=0.431;exitY=0.014;exitDx=0;exitDy=0;strokeWidth=3;exitPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-35" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="937" y="-291.25" as="sourcePoint" />
<mxPoint x="914" y="-277" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-38" value="SingleUse" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="329" y="-700.0000000000001" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-169" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-39" target="W42QqZGia2SB_BN2uIFJ-141" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-39" value="ManaScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="509" y="-230" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-159" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-41" target="W42QqZGia2SB_BN2uIFJ-38" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="414" y="-676" as="targetPoint" />
<Array as="points">
<mxPoint x="419" y="-676" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-41" value="Quantity" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="494" y="-700" width="95" height="48" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-153" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-43" target="W42QqZGia2SB_BN2uIFJ-130" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="464" y="-612" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-43" value="Price" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="329" y="-637.0000000000001" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-170" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-45" target="W42QqZGia2SB_BN2uIFJ-142" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-45" value="Range" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="44" y="-437" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-163" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-47" target="W42QqZGia2SB_BN2uIFJ-142" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-47" value="WeaponType" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="104" y="-369.12" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-161" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-49" target="W42QqZGia2SB_BN2uIFJ-141" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-49" value="ItemType" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="329" y="-240.32" width="90" height="48" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-165" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-53" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="414" y="-740" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-53" value="Equipped" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="494" y="-765" width="92.5" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-164" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-55" target="W42QqZGia2SB_BN2uIFJ-141" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-55" value="BodyPart" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="414" y="-207" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-57" value="Worn" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="409" y="-830" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-59" value="Wearable" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="329" y="-764" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-171" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-62" target="W42QqZGia2SB_BN2uIFJ-143" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-62" value="HealingScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="584" y="-333.55999999999995" width="100" height="51.25" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-168" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-63" target="W42QqZGia2SB_BN2uIFJ-142" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-63" value="AttackScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="314" y="-370" width="90" height="50.88" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-167" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-66" target="W42QqZGia2SB_BN2uIFJ-142" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-66" value="DefendScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="214" y="-336.25" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-68" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-69" target="W42QqZGia2SB_BN2uIFJ-1" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="994" y="-877" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-69" value="Account_Number" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;fontStyle=4" parent="1" vertex="1">
<mxGeometry x="914" y="-967" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-71" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="994" y="-877" as="targetPoint" />
<Array as="points">
<mxPoint x="1124" y="-877" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-71" value="Name" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1089" y="-957" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-72" value="Forename" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1116.5" y="-1030" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-73" value="Surname" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1195" y="-980" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-74" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=3;" parent="1" source="W42QqZGia2SB_BN2uIFJ-72" target="W42QqZGia2SB_BN2uIFJ-71" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1392" y="-897" as="sourcePoint" />
<mxPoint x="1420" y="-827" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-75" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=3;entryX=0.013;entryY=0.532;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-73" target="W42QqZGia2SB_BN2uIFJ-71" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1208" y="-864" as="sourcePoint" />
<mxPoint x="1243" y="-845" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-76" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-77" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="884" y="-877" as="targetPoint" />
<Array as="points">
<mxPoint x="895" y="-1007" />
<mxPoint x="895" y="-877" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-77" value="EMailAddress" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="944" y="-1040" width="130" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-78" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-79" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="914" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="994" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-79" value="Type" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="959" y="-484" width="70" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-80" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.632;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-81" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="914" y="-534" />
</Array>
<mxPoint x="864.0000000000005" y="-534.0100000000002" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-81" value="Level" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="879" y="-484" width="70" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-83" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="864.0000000000005" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1182" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-83" value="ExperiencePoints" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1101.5" y="-619.65" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-84" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-85" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="865" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1084" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-85" value="MaxHealth" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1015" y="-674" width="160" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-86" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-87" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1364" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1409" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-87" value="Health" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1359" y="-434" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-89" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1154" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1335" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-89" value="AttackInScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1275" y="-479" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-91" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1039" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1205" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-91" value="DefenceScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1161.5" y="-484" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-93" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="864" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1095" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-93" value="StealthScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1045" y="-484" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-95" value="ManaScore" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1424" y="-484" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-96" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-97" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1194" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1290" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-97" value="MoneyBank" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1234" y="-689" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-98" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-99" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1284" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="1364" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-99" value="MoneyWallet" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1314" y="-639" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-178" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.605;entryY=-0.016;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-100" target="W42QqZGia2SB_BN2uIFJ-119" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-100" value="Creation" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1469" y="-703.5" width="70" height="42" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-179" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-101" target="W42QqZGia2SB_BN2uIFJ-119" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-101" value="Expiry" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;" parent="1" vertex="1">
<mxGeometry x="1543.5" y="-695.5" width="66.5" height="37" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-122" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.302;entryY=0.053;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-102" target="W42QqZGia2SB_BN2uIFJ-119" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1456" y="-677.5" />
<mxPoint x="1456" y="-650.5" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-102" value="BillDuration" style="ellipse;whiteSpace=wrap;html=1;fontSize=14;strokeWidth=3;direction=west;dashed=1;dashPattern=1 1;" parent="1" vertex="1">
<mxGeometry x="1414" y="-751.5" width="85" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-106" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="762.72" y="-463.25" width="76" height="75" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-107" value="" style="rhombus;whiteSpace=wrap;html=1;" parent="W42QqZGia2SB_BN2uIFJ-106" vertex="1">
<mxGeometry width="76" height="75" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-108" value="Attacker/ Defender" style="rhombus;whiteSpace=wrap;html=1;" parent="W42QqZGia2SB_BN2uIFJ-106" vertex="1">
<mxGeometry x="2.111111111111111" y="3.2522727272727145" width="71.77777777777777" height="68.50227272727273" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-109" value="M" style="endArrow=none;html=1;rounded=0;strokeWidth=2;edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.369;entryY=0.993;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-107" target="W42QqZGia2SB_BN2uIFJ-15" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="855" y="-447" as="sourcePoint" />
<mxPoint x="934" y="-497" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-110" value="N" style="endArrow=none;html=1;rounded=0;strokeWidth=2;shape=link;edgeStyle=orthogonalEdgeStyle;exitX=-0.011;exitY=0.349;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-25" target="W42QqZGia2SB_BN2uIFJ-107" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="854" y="-382" as="sourcePoint" />
<mxPoint x="904" y="-432" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-111" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-112" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="973.5" y="-534" as="targetPoint" />
<Array as="points">
<mxPoint x="994" y="-534" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-112" value="&lt;span style=&quot;border-bottom: 1px dotted&quot;&gt;Name&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="944" y="-597" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-113" value="&lt;span style=&quot;border-bottom: 1px dotted&quot;&gt;BattleNo&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="1275" y="-359.12" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-190" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=0;endArrow=none;endFill=0;jumpStyle=none;jumpSize=6;" parent="1" source="W42QqZGia2SB_BN2uIFJ-119" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1404" y="-530" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-119" value="Date" style="ellipse;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="1424" y="-621.65" width="100" height="59.65" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-140" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-128" target="W42QqZGia2SB_BN2uIFJ-141" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="459" y="-394.75" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-128" value="ISA" style="verticalLabelPosition=middle;verticalAlign=middle;html=1;shape=mxgraph.basic.acute_triangle;dx=0.5;strokeWidth=3;labelPosition=center;align=center;" parent="1" vertex="1">
<mxGeometry x="414" y="-470" width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-130" value="&lt;span style=&quot;border-bottom: 1px dotted&quot;&gt;ItemName&lt;/span&gt;" style="ellipse;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="494" y="-632" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-141" value="Wear" style="shape=ext;margin=3;double=1;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="409" y="-310" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-145" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-142" target="W42QqZGia2SB_BN2uIFJ-128" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-142" value="Weapon" style="shape=ext;margin=3;double=1;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="214" y="-428.25" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-144" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-143" target="W42QqZGia2SB_BN2uIFJ-128" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-143" value="Food" style="shape=ext;margin=3;double=1;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="584" y="-428.25" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-146" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=45;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-18" target="W42QqZGia2SB_BN2uIFJ-128" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-160" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-18" target="W42QqZGia2SB_BN2uIFJ-57" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="458" y="-790" as="targetPoint" />
<mxPoint x="459" y="-580" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-173" value="1" style="endArrow=none;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;strokeWidth=2;fontSize=14;" parent="1" source="W42QqZGia2SB_BN2uIFJ-1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="691.5000000000005" y="-763.845" as="sourcePoint" />
<mxPoint x="814" y="-720" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-187" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=45;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-174" target="W42QqZGia2SB_BN2uIFJ-184" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-174" value="ISA" style="verticalLabelPosition=middle;verticalAlign=middle;html=1;shape=mxgraph.basic.acute_triangle;dx=0.5;strokeWidth=3;labelPosition=center;align=center;" parent="1" vertex="1">
<mxGeometry x="873" y="-679" width="90" height="60" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-180" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-15" target="W42QqZGia2SB_BN2uIFJ-174" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-189" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="W42QqZGia2SB_BN2uIFJ-184" target="W42QqZGia2SB_BN2uIFJ-188" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-184" value="Mage" style="shape=ext;margin=3;double=1;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="919" y="-751.5" width="100" height="41.5" as="geometry" />
</mxCell>
<mxCell id="W42QqZGia2SB_BN2uIFJ-188" value="ManaScore" style="ellipse;whiteSpace=wrap;html=1;align=center;strokeWidth=3;" parent="1" vertex="1">
<mxGeometry x="1054" y="-799" width="100" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

File diff suppressed because one or more lines are too long

BIN
hk1234566/SQL/Assessment1/update3/ERD-Page-2.drawio.png (Stored with Git LFS) Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
hk1234566/SQL/Assessment1/update3/ERD-characters.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/update3/ERD-combat.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/update3/ERD-items-1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/update3/ERD-items-2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/update3/ERD-items.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment1/update3/ERD-overview.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
hk1234566/SQL/Assessment2/1NF-example.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment2/2NF-example.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment2/3NF-example.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment2/4NF-example.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment2/5NF-example.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
hk1234566/SQL/Assessment2/first-normal-form.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,30 @@
2) Write relational schema and integrity constraints for your relations. Provide the relevant SQL DDL statements to implement your ERD.
a) Pay careful attention to your choice of datatypes and consistency.
b) Consider referential integrity and nullable/not nullable attributes.
c) Make comment on the Normalisation level of your schemas.
d) Providing relational algebra statements is a bonus.
Assessment:
2. SQL DDL (30%)
You will provide a PDF Þle that contains your
Relational Schemas (RS),
Integrity Constraints (IC),
i.e.:
```
Mec_Repair(MID:integer, Name:string, Brand:string),
ICs: MID (Primary key), Brand (Foreign key referencing Car)
Normalisation reports: i.e. 3NF
DDL statements:
Car(Brand:string, weight:integer),
ICs: Brand (Primary key)
Normalisation reports: i.e. 3NF
DDL statements:
```
Normalisation reports for each RSs and
the DDL statements written in SQL.
You will receive full mark if your RS and ICs matches with your ERD. Otherwise, you we will reduce your mark based on the followings:
a) Typo for SQL code -2pts.
b) Reporting wrong Normalisation level -5pts.
c) IC mismatch from ERD to ICs or from ICs to DDL -10 pts.
d) Providing Relational Algebra statements (Bonus 10 pts)

BIN
hk1234566/SQL/Assessment2/second-nromal-form.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
3. Java Library and SQL queries (40%)
You will provide a java library that reads CSV Þle and builds required tables and answers the queries given above. You will receive full mark if your library successfully accomplishes these tasks. Otherwise we will reduce your mark according to the following rules:
a) Not compiles -100pts.
b) Compiles but cannot populate tables -50pts.
c) Cannot read CSV Þles but reads from other Þle format and populates tables - 0pts.
d) SQL Integrity Constraint warning, error while populating -10pts.
e) Failed to retrieve the answer for a query -10pts.

8
hk1234566/SQL/Combat.csv Normal file
View File

@@ -0,0 +1,8 @@
BattleDate,BattleNo,Attacker,Defender,Weapon,Result,Damage
15-May-23,5481,Calvin,SneakySue,Axe,Hit,87
15-May-23,5481,SneakySue,Calvin,Dagger,Miss,0
15-May-23,5481,Calvin,SneakySue,Axe,Hit,94
15-May-23,5481,SneakySue,Calvin,Dagger,Parry,0
15-May-23,5481,Calvin,SneakySue,Axe ,Hit,78
15-May-23,5481,SneakySue,Calvin,Dagger,Hit,14
15-May-23,5481,Calvin,SneakySue,Axe,Victory,112
1 BattleDate BattleNo Attacker Defender Weapon Result Damage
2 15-May-23 5481 Calvin SneakySue Axe Hit 87
3 15-May-23 5481 SneakySue Calvin Dagger Miss 0
4 15-May-23 5481 Calvin SneakySue Axe Hit 94
5 15-May-23 5481 SneakySue Calvin Dagger Parry 0
6 15-May-23 5481 Calvin SneakySue Axe Hit 78
7 15-May-23 5481 SneakySue Calvin Dagger Hit 14
8 15-May-23 5481 Calvin SneakySue Axe Victory 112

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,8 @@
Account_Number,Forename,surname,e-mail_Address,Character_CreationDate,Character_Expiry_Date,Character_Name,Character_Type,Level,ExperiencePoints,Max_Health,Health,AttackinScore,DefenceScore,StealthScore,ManaScore,Money_bank,Money_wallet
15468336,Jones,Susan,sj56@gmail.com,02-Jan-2022,,juju97,Warrior,12,12475,1000,996,85,23,17,,4273.95,351.00
35482956,Turker,Uraz,u.turker@lancaster.ac.uk,05-Mar-2022,,Uraz-The-Magnificent,Mage,34,34925,1200,1094,34,49,21,64,11397.00,700.00
64345236,Ricki,Boswell-Challand,r.boswell-challand@lancaster.ac.uk,24-Apr-2022,,Calvin,Merchant,18,21765,950,950,54,53,36,,34950.00,3705.00
64345236,Ricki,Boswell-Challand,r.boswell-challand@lancaster.ac.uk,13-May2022,,Hobbes,Warrior,95,95678,1500,1500,88,79,84,,0,0
15468336,Jones,Susan,sj56@gmail.com,15-Mar-2022,,SneakySue,Thief,7,7201,900,856,37,12,56,,11730.34,295.00
54387937,Schmidt,Johan,jjs46@hotmail.com,08-Jan2023,,SpacemanSpiff,Mage,3,1085,250,227,09,19,8,31,548.00,102.00
15468336,Jones,Susan,sj56@gmail.com,14-Jul-2022,19-Aug-2022,DarkMage,Mage,3,2012,250,250,12,17,9,21,0.00,0.00
1 Account_Number Forename surname e-mail_Address Character_CreationDate Character_Expiry_Date Character_Name Character_Type Level ExperiencePoints Max_Health Health AttackinScore DefenceScore StealthScore ManaScore Money_bank Money_wallet
2 15468336 Jones Susan sj56@gmail.com 02-Jan-2022 juju97 Warrior 12 12475 1000 996 85 23 17 4273.95 351.00
3 35482956 Turker Uraz u.turker@lancaster.ac.uk 05-Mar-2022 Uraz-The-Magnificent Mage 34 34925 1200 1094 34 49 21 64 11397.00 700.00
4 64345236 Ricki Boswell-Challand r.boswell-challand@lancaster.ac.uk 24-Apr-2022 Calvin Merchant 18 21765 950 950 54 53 36 34950.00 3705.00
5 64345236 Ricki Boswell-Challand r.boswell-challand@lancaster.ac.uk 13-May2022 Hobbes Warrior 95 95678 1500 1500 88 79 84 0 0
6 15468336 Jones Susan sj56@gmail.com 15-Mar-2022 SneakySue Thief 7 7201 900 856 37 12 56 11730.34 295.00
7 54387937 Schmidt Johan jjs46@hotmail.com 08-Jan2023 SpacemanSpiff Mage 3 1085 250 227 09 19 8 31 548.00 102.00
8 15468336 Jones Susan sj56@gmail.com 14-Jul-2022 19-Aug-2022 DarkMage Mage 3 2012 250 250 12 17 9 21 0.00 0.00

15
hk1234566/SQL/Items.csv Normal file
View File

@@ -0,0 +1,15 @@
Character,Item,Item_Type,WeaponType,Range,Price,Quantity,DefendScore,AttackScore,HealingScore,ManaScore,SingleUse,wearable,worn,BodyPart,Equipped
juju97,Broadsword,Weapon,Melee,,175,1,,24,,,0,0,,,1
juju97,breastplate,Armour,,,290,1,18,,,,0,1,1,torso,0
juju97,slingshot,Weapon,Projectile,30,50,1,,13,,,0,0,,,0
Uraz-The-Magnificent,Wizard's Staff,Artefact,,,3500,1,0,0,,37,0,,,,1
Uraz-The-Magnificent,Wizard's Robe,Clothing,,,2000,1,10,,,,,1,1,torso,0
juju97,Tunic,Clothing,,,5,1,1,0,,,0,1,1,torso,
Uraz-The-Magnificent,Apple,Food,,,1,1,,,10,,1,,,,0
juju97,Bread,Food,,,0.75,3,,,8,,1,,,,0
SneakySue,Dagger,Weapon,Melee,,12,1,1,10,0,,0,0,,,1
SpacemanSpiff,Transmogrifier,Artefact,,,54000,1,,,,500,0,0,,,1
SpacemanSpiff,Can of Tuna,Food,,,3,4,,,15,,1,0,,,1
Calvin,Axe,Weapon,Melee,,80,1,,,,,,,,,1
juju97,Cloak of Invisibility,Clothing,,,4500,1,,,,75,,1,0,torso,
Uraz-The-Magnificent,Cheese,Food,,,6,10,,,12,,1,0,0,,0
1 Character Item Item_Type WeaponType Range Price Quantity DefendScore AttackScore HealingScore ManaScore SingleUse wearable worn BodyPart Equipped
2 juju97 Broadsword Weapon Melee 175 1 24 0 0 1
3 juju97 breastplate Armour 290 1 18 0 1 1 torso 0
4 juju97 slingshot Weapon Projectile 30 50 1 13 0 0 0
5 Uraz-The-Magnificent Wizard's Staff Artefact 3500 1 0 0 37 0 1
6 Uraz-The-Magnificent Wizard's Robe Clothing 2000 1 10 1 1 torso 0
7 juju97 Tunic Clothing 5 1 1 0 0 1 1 torso
8 Uraz-The-Magnificent Apple Food 1 1 10 1 0
9 juju97 Bread Food 0.75 3 8 1 0
10 SneakySue Dagger Weapon Melee 12 1 1 10 0 0 0 1
11 SpacemanSpiff Transmogrifier Artefact 54000 1 500 0 0 1
12 SpacemanSpiff Can of Tuna Food 3 4 15 1 0 1
13 Calvin Axe Weapon Melee 80 1 1
14 juju97 Cloak of Invisibility Clothing 4500 1 75 1 0 torso
15 Uraz-The-Magnificent Cheese Food 6 10 12 1 0 0 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
https://sqliteonline.com/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

42
hk1234566/SQL/notes.md Normal file
View File

@@ -0,0 +1,42 @@
Players.csv - The main player and character data Þle, which contains player character definitions, with customer and billing data.
Items.csv - Character inventory of weapons.
Combat.csv - Records the activity and results of battles between players.
1. Your initial E-R diagram (WEEK 8 30%)
You will provide two outputs:
1) An English paragraph given in a PDF Þle that describes your personal ERD and
2) your ERD (embedded in the PDF Þle). We will assess your ERD by reading your paragraph.
You will receive full mark if your paragraph matches to your ERD.
Otherwise we will reduce your marks based on the following rules:
a) For each participation constraint fault (-1.5%).
b) For each multiplicity fault (-1.5%).
c) For each notation/Symbol fault (-1.5%).
2. SQL DDL ( WEEK 9 30%)
You will provide a PDF Þle that contains your
Relational Schemas (RS),
Integrity Constraints (IC),
Normalisation reports for each RSs and
the DDL statements written in SQL.
You will receive full mark if
your RS and ICs matches with your ERD.
Otherwise, you we will reduce your mark based on the followings:
a) For each typo for SQL code (-0.6%).
b) Reporting wrong(or none) Normalisation level (for each table -1.5%).
c) For each IC mismatch (from ERD to ICs) and (from ICs to DDL) (-3%).
3. Java Library and SQL queries (WEEK 10 40%)
You will provide a PDF Þle that contains the English versions of your queries.
You will also provide a java library that reads CSV Þle and
builds required tables and answers the queries you provided.
You will receive full mark if your library successfully accomplishes these tasks.
Otherwise we will reduce your mark according to the following rules:
a) Not compiles (-40%).
b) Compiles but cannot populate tables (-20%).
c) Cannot read CSV Þles but read from other Þle format and populates tables (-0%).
d) For each SQL warning, error (-3%).
e) For each failed query (-3%).
f) Providing Relational Algebra statements (for each query) (+3%)

7
hk1234566/gitUpdate.bat Normal file
View File

@@ -0,0 +1,7 @@
git status .
@pause
git add .
git commit -m"update hk1234566,"
start git push

15
hk1234566/meta.md Normal file
View File

@@ -0,0 +1,15 @@
---
tags: [python, wireshark, networking, javascript, SQL, "SCC 212"]
---
# hk1234566
## balance history
### quotation
佢份問題總共有五個部份(其中有兩個部份比分)
我諗我會報 HKD 250
如果合意的話麻煩您比埋 NetworkApplications.py 呢個 file 我好嘛
唔該先 😊

13
hk1234566/package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "hk1234566",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"gitUpdate": "git add . && git commit -m\"update hk1234566,\" && git push"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@@ -0,0 +1,685 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
######
import argparse
import socket
import os
import sys
import struct
import time
def setupArgumentParser() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description='A collection of Network Applications developed for SCC.203.')
parser.set_defaults(func=ICMPPing, hostname='lancaster.ac.uk')
subparsers = parser.add_subparsers(help='sub-command help')
parser_p = subparsers.add_parser('ping', aliases=['p'], help='run ping')
parser_p.add_argument('hostname', type=str, help='host to ping towards')
parser_p.add_argument('count', nargs='?', type=int,
help='number of times to ping the host before stopping')
parser_p.add_argument('timeout', nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_p.set_defaults(func=ICMPPing)
parser_t = subparsers.add_parser('traceroute', aliases=['t'],
help='run traceroute')
parser_t.add_argument('hostname', type=str, help='host to traceroute towards')
parser_t.add_argument('timeout', nargs='?', type=int,
help='maximum timeout before considering request lost')
parser_t.add_argument('protocol', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_t.set_defaults(func=Traceroute)
parser_w = subparsers.add_parser('web', aliases=['w'], help='run web server')
parser_w.set_defaults(port=8080)
parser_w.add_argument('port', type=int, nargs='?',
help='port number to start web server listening on')
parser_w.set_defaults(func=WebServer)
parser_x = subparsers.add_parser('proxy', aliases=['x'], help='run proxy')
parser_x.set_defaults(port=8000)
parser_x.add_argument('port', type=int, nargs='?',
help='port number to start web server listening on')
parser_x.set_defaults(func=Proxy)
args = parser.parse_args()
return args
class NetworkApplication:
def checksum(self, dataToChecksum: str) -> str:
csum = 0
countTo = (len(dataToChecksum) // 2) * 2
count = 0
while count < countTo:
thisVal = dataToChecksum[count+1] * 256 + dataToChecksum[count]
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2
if countTo < len(dataToChecksum):
csum = csum + dataToChecksum[len(dataToChecksum) - 1]
csum = csum & 0xffffffff
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
answer = socket.htons(answer)
return answer
def printOneResult(self, destinationAddress: str, packetLength:
int, time: float, ttl: int, destinationHostname=''):
if destinationHostname:
print("%d bytes from %s (%s):ttl=%d time=%.2f ms" % (packetLength, destinationHostname, destinationAddress, ttl, time))
else:
print("%d bytes from %s: ttl=%dtime=%.2f ms" % (packetLength, destinationAddress, ttl, time))
def printAdditionalDetails(self, packetLoss=0.0, minimumDelay=0.0,averageDelay=0.0, maximumDelay=0.0):
print("%.2f%% packet loss" % (packetLoss))
if minimumDelay > 0 and averageDelay > 0 and maximumDelay > 0:
print("rtt min/avg/max = %.2f/%.2f/%.2fms" % (minimumDelay, averageDelay, maximumDelay))
class ICMPPing(NetworkApplication):
def receiveOnePing(self, icmpSocket, destinationAddress, ID,timeout):
# 1. Wait for the socket to receive a reply
timeLeft = timeout/1000
select = 0
startedSelect = time.time()
whatReady = select.select([icmpSocket],[],[],timeLeft)
howLongInSelect =(time.time() - startedSelect)
# 2. Once received, record time of receipt, otherwise, handle a timeout
if whatReady[0] == []:#timeout
return None
timeLeft = timeLeft - howLongInSelect
if timeLeft <= 0:
return None
recPacket, addr = icmpSocket.recvfrom(ICMP_MAX_RECV)
timeRecieved = time.time()
icmpHeader = recPacket[20:28]
# 3. Compare the time of receipt to time of sending, producing the total network delay
timeSent = self.sendOnePing(icmpSocket, destinationAddress, 111)
Delay = timeRecieved - timeSent
# 4. Unpack the packet header for useful information, including the ID
icmpType,icmpCode,icmpChecksum,icmpPacketID,icmpSeqNumber = struct.unpack("bbHHh",icmpHeader)
# 5. Check that the ID matches between the request and reply
# 6. Return total network delay
if(icmpPacketID == ID):
return addr[0].Delay
else:
return 0
def sendOnePing(self, icmpSocket, destinationAddress, ID):
# 1. Build ICMP header
Type = 8
code = 0
chksum = 0
seq = 1
data = "data"
icmpHeader = struct.pack("bbHHh", Type, code,chksum, ID,seq)
# 2. Checksum ICMP packet using given function
real_chksum = self.checksum(icmpHeader)
# 3. Insert checksum into packet
icmpheader = struct.pack("bbHHh", type,code,real_chksum,ID,seq)
packet = icmpHeader
# 4. Send packet using socket
icmpSocket.sendto(packet, (destinationAddress,1) ) #double check this //run with wireshark
# 5. Record time of sending
sent_time = time.time()
return sent_time
def doOnePing(self, destinationAddress, timeout):
# 1. Create ICMP socket
ICMP_CODE = socket.getprotobyname("icmp") #Translate an Internet protocol name (for example, 'icmp') to a constant suitable for passing as the (optional) third argument to the socket() function.
icmpSocket = socket.socket(socket.AF_INET,socket.SOCK_RAW, ICMP_CODE)
# 2. Call sendOnePing function
timeSent = self.sendOnePing(icmpSocket, destinationAddress, 111)
# 3. Call receiveOnePing function
AddressAndDelay = self.receiveOnePing(icmpSocket, destinationAddress, 111, 1000,timeSent)
# 4. Close ICMP socket
icmpSocket.close()
# 5. Return total network delay
return AddressAndDelay[0], AddressAndDelay[1]
def __init__(self, args):
print('Ping to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
ip_address = socket.gethostbyname(args.hostname)
# 2. Call doOnePing function approximately every second
while True:
time.sleep(1)
testVariable = args.timeout
print("testing:", testVariable)
recAddressAndDelay = self.doOnePing(ip_address, testVariable, 1)
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
self.printOneResult(ip_address, 50, recAddressAndDelay[1]*1000,150)
#Example use of printOneResult - complete as appropriate
# 4. Continue this process until stopped - would this be a loop? and when should we stop?
class Traceroute(NetworkApplication):
def __init__(self, args):
#
# Please ensure you print each result using the printOneResult method!
print('Traceroute to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
ip_address = socket.gethostbyname(args.hostname)
# 2. Call PingOneNode function approximately every second
while True:
time.sleep(1)
nodalDelay = self.pingOneNode(ip_address,args.timeout,1)
# 4. Continue this process until stopped - until ICMP = 0
if ICMP == 0:
break
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
self.printOneResult(ip_address, 50, nodalDelay[1]*1000,150) #check this don't think its right
def pingOneNode():
# 1. Create ICMP socket
ICMP_CODE = socket.getprotobyname("icmp") #Translate an Internet protocol name (for example, 'icmp') to a constant suitable for passing as the (optional) third argument to the socket() function.
icmpSocket = socket.socket(socket.AF_INET,socket.SOCK_RAW, ICMP_CODE)
# 2. Call sendNodePing function
timeSent = self.sendNodePing(icmpSocket, destinationAddress, 111)
# 3. Call recieveNodePing function
AddressAndDelay = self.recieveNodePing(icmpSocket, destinationAddress, 111, 1000,timeSent)
# 4. Close ICMP socket
icmpSocket.close()
# 5. Return total network delay- add up all the nodes
for x in Nodes:
TotalDelay = (AddressAndDelay[x] + AddressAndDelay[x +1])
if x == "numberOfNodes":
break
return TotalDelay
def sendNodePing():
# 1. Build ICMP header
Type = 8
code = 0
chksum = 0
seq = 1
data = "data"
icmpHeader = struct.pack("bbHHh", Type, code,chksum, ID,seq)
# 2. Checksum ICMP packet using given function
real_chksum = self.checksum(icmpHeader)
# 3. Insert checksum into packet
icmpheader = struct.pack("bbHHh", type,code,real_chksum,ID,seq)
packet = icmpHeader
# 4. Send packet using socket
icmpSocket.sendto(packet, (destinationAddress,1) ) #double check this //run with wireshark
# 5. Record time of sending
sentTime = time.time()
return sentTime
def recieveNodePing():
# 1. Wait for the socket to receive a reply- TTL = 0
timeLeft = timeout/1000
select = 0
startedSelect = time.time()
whatReady = select.select([icmpSocket],[],[],timeLeft)
howLongInSelect =(time.time() - startedSelect)
# 2. Once received, record time of receipt, otherwise, handle a timeout
if TTL != 0:#timeout
return None
timeLeft = timeLeft - howLongInSelect
if TTL == 0:
recPacket, addr = icmpSocket.recvfrom(ICMP_MAX_RECV)
timeRecieved = time.time()
icmpHeader = recPacket[20:28]
return timeLeft
# 3. Compare the time of receipt to time of sending, producing the total network delay
timeSent = self.sendNodePing(icmpSocket, destinationAddress, 111)
Delay = timeRecieved - timeSent
# 4. Unpack the packet header for useful information, including the ID
icmpType,icmpCode,icmpChecksum,icmpPacketID,icmpSeqNumber = struct.unpack("bbHHh",icmpHeader)
# 5. Check that the ID matches between the request and reply
# 6. Return total network delay
if(icmpPacketID == ID):
return pingOneNode.TotalDelay
else:
return 0
class WebServer(NetworkApplication):
def handleRequest(tcpSocket):
# 1. Receive request message from the client on connection socket
bufferSize = tcpSocket.CMSG_SPACE(4) #IPv4 address is 4 bytes in length
requestMessage = tcpSocket.recvmsg(bufferSize[0,[0]])
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
file = requestMessage.unpack_from(format, buffer, offset=1) #returns a tuple
# 3. Read the corresponding file from disk
socket.sendfile(file, offset=0, count=None)
# 4. Store in temporary buffer
buffer = socket.makefile(mode='r', buffering=None, encoding=None,errors=None, newline=None)
struct.pack_into(format, self.buffer, 0, file)
# 5. Send the correct HTTP response error
# 6. Send the content of the file to the socket
tcpSocket.recvmsg(bufferSize[0, 0])
# 7. Close the connection socket
tcpSocket.close()
pass
def __init__(self, args):
print('Web Server starting on port: %i...' % (args.port))
# 1. Create server socket
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("creating server socket")
# 2. Bind the server socket to server address and server port
serverSocket.bind((socket.gethostname(), 80))
print("binding socket")
# 3. Continuously listen for connections to server socket
serverSocket.listen(5)
# 4. When a connection is accepted, call handleRequest function, passing new connection socket (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
newSocket = socket.accept()
while True:
handleRequest(newSocket)
print("calling handleRequest")
# 5. Close server socket
serverSocket.close()
class Proxy(NetworkApplication):
def __init__(self, args):
print('Web Proxy starting on port: %i...' % (args.port))
if __name__ == "__main__":
args = setupArgumentParser()
args.func(args)
# 1. Receive request message from the client on connection socket
# IPv4 address is 4 bytes in length
bufferSize = connectionSocket.CMSG_SPACE(4)
requestMessage = connectionSocket.recvmsg(bufferSize[0, [0]])
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
file = requestMessage.unpack_from( format, buffer, offset = 1) # returns a tuple
# 2. send HTTP request for object to proxy server
httpRequest= ("GET /" + file + " HTTP/1.1\r\n\r\n")
connectionSocket.send(httpRequest.encode())
#connctionSocket.send("HTTP/1.1 200 OK\r\n\r\n")
print("Request message sent")
# 3. proxy server checks to see if copy of object is stored locally- calls class localObject
filename= requestMessage.split()[1]
try:
isObjectLocal=open(filename[1:], "r") # open file in text mode
# 1. if it does, the proxy server returns the object within a HTTP response message to the client browser
# 3. Read the corresponding file from disk
socket.sendfile(object, offset = 0, count =None)
#send via HTTP response message to client Browser
except isObjectLocal == "false":
# 2. if it doesnt, the proxy server opens a TCP connection to the origin server:
proxySocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bind the socket to a public host, and a well-known port
proxySocket.bind((socket.gethostname(), 80))
#sends HTTP request for object
proxySocket.send(httpRequest.encode())
#origin server recieves request
connectionSocket.recvmessage(httpRequest.encode())
# 4. proxy server sends HTTP request for the object into the cache-to-server TCP connection
# 5. origin server receives request
# 6. origin server sends object to proxy server within a HTTP response
# 7. proxy server receives the object
object= serverSocket.recvmsg(bufferSize[0, 0])
# 8. proxy server stores copy in its local storage
# 9. proxy server sends copy -in HTTP response message- to client browser over TCP connection
# proxy server checks to see if copy of object is stored locally

View File

@@ -0,0 +1,2 @@
[FORMAT]
max-line-length=180

View File

@@ -0,0 +1,587 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import argparse
import socket
import os
import sys
import struct
import time
import random
import traceback # useful for exception handling
import threading
from pprint import pprint
# config
INCOMING_BUFFER = 1024
OUTGOING_BUFFER = INCOMING_BUFFER * 10
ICMP_TYPE = 8
def setupArgumentParser() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description='A collection of Network Applications developed for SCC.203.')
parser.set_defaults(func=ICMPPing, hostname='lancaster.ac.uk')
subparsers = parser.add_subparsers(help='sub-command help')
parser_p = subparsers.add_parser('ping', aliases=['p'], help='run ping')
parser_p.set_defaults(timeout=4)
parser_p.add_argument('hostname', type=str, help='host to ping towards')
parser_p.add_argument(
'--count',
'-c',
nargs='?',
type=int,
help='number of times to ping the host before stopping')
parser_p.add_argument(
'--timeout',
'-t',
nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_p.set_defaults(func=ICMPPing)
parser_t = subparsers.add_parser('traceroute', aliases=['t'],
help='run traceroute')
parser_t.set_defaults(timeout=4, protocol='icmp')
parser_t.add_argument(
'hostname',
type=str,
help='host to traceroute towards')
parser_t.add_argument(
'--timeout',
'-t',
nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_t.add_argument('--protocol', '-p', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_t.set_defaults(func=Traceroute)
parser_pt = subparsers.add_parser('paris-traceroute', aliases=['pt'],
help='run paris-traceroute')
parser_pt.set_defaults(timeout=4, protocol='icmp')
parser_pt.add_argument(
'hostname',
type=str,
help='host to traceroute towards')
parser_pt.add_argument(
'--timeout',
'-t',
nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_pt.add_argument('--protocol', '-p', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_pt.set_defaults(func=ParisTraceroute)
parser_w = subparsers.add_parser(
'web', aliases=['w'], help='run web server')
parser_w.set_defaults(port=8080)
parser_w.add_argument('--port', '-p', type=int, nargs='?',
help='port number to start web server listening on')
parser_w.set_defaults(func=WebServer)
parser_x = subparsers.add_parser('proxy', aliases=['x'], help='run proxy')
parser_x.set_defaults(port=8000)
parser_x.add_argument('--port', '-p', type=int, nargs='?',
help='port number to start web server listening on')
parser_x.set_defaults(func=Proxy)
args = parser.parse_args()
return args
class NetworkApplication:
def checksum(self, dataToChecksum: str) -> str:
csum = 0
countTo = (len(dataToChecksum) // 2) * 2
count = 0
while count < countTo:
thisVal = dataToChecksum[count + 1] * 256 + dataToChecksum[count]
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2
if countTo < len(dataToChecksum):
csum = csum + dataToChecksum[len(dataToChecksum) - 1]
csum = csum & 0xffffffff
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
answer = socket.htons(answer)
return answer
def printOneResult(
self,
destinationAddress: str,
packetLength: int,
time: float,
ttl: int,
destinationHostname=''):
if destinationHostname:
print(
"%d bytes from %s (%s): ttl=%d time=%.2f ms" %
(packetLength, destinationHostname, destinationAddress, ttl, time))
else:
print("%d bytes from %s: ttl=%d time=%.2f ms" %
(packetLength, destinationAddress, ttl, time))
def printAdditionalDetails(
self,
packetLoss=0.0,
minimumDelay=0.0,
averageDelay=0.0,
maximumDelay=0.0):
print("%.2f%% packet loss" % (packetLoss))
if minimumDelay > 0 and averageDelay > 0 and maximumDelay > 0:
print("rtt min/avg/max = %.2f/%.2f/%.2f ms" %
(minimumDelay, averageDelay, maximumDelay))
def printMultipleResults(
self,
ttl: int,
destinationAddress: str,
measurements: list,
destinationHostname=''):
latencies = ''
noResponse = True
for rtt in measurements:
if rtt is not None:
latencies += str(round(rtt, 3))
latencies += ' ms '
noResponse = False
else:
latencies += '* '
if noResponse is False:
print(
"%d %s (%s) %s" %
(ttl,
destinationHostname,
destinationAddress,
latencies))
else:
print("%d %s" % (ttl, latencies))
class ICMPPing(NetworkApplication):
# Task 1.1: ICMP Ping
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
# 1. Wait for the socket to receive a reply
icmpSocket.settimeout(timeout)
try:
reply, addr = icmpSocket.recvfrom(2048)
except socket.timeout as msg:
print("No data received from socket within timeout period. Message: " + str(msg))
sys.exit(1)
# 2. Once received, record time of receipt, otherwise, handle a timeout
recv_time = time.time()
# 4. Unpack the packet header for useful information, including the ID
# icmp header of the received packet,
# bottom of packet because of network byte-order
# align offset to include the layer 2 encap = 14
reply_size = struct.unpack(">H", reply[16 - 14:18 - 14])[0]
reply_ttl = struct.unpack(">B", reply[22 - 14:23 - 14])[0]
reply_id = struct.unpack(">B", reply[38 - 14:39 - 14])[0]
# 5. Check that the ID matches between the request and reply
if reply_id != ID:
print("Received packet ID not match")
sys.exit(1)
# 6. Return recv time + packet size
return (recv_time, reply_size, reply_ttl)
def sendOnePing(self, icmpSocket, destinationAddress, ID):
# 1. Build ICMP header
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, ID, 1)
data = bytes("Task 1.1: ICMP Ping", 'utf-8')
# 2. Checksum ICMP packet using given function
new_checksum = self.checksum(header + data)
# 3. Insert checksum into packet
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, ID, 1)
packet = header + data
# 4. Send packet using socket
while packet:
sent = icmpSocket.sendto(packet, (destinationAddress, 1500)) # 1500 = Port number
packet = packet[sent:]
# 5. Record time of sending
sendTime = time.time()
return sendTime
def doOnePing(self, destinationAddress, timeout):
# 1. Create ICMP socket
# Sends raw packets to ipv4 addresses
new_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))
ID = 1
# 2. Call sendOnePing function
sendTime = self.sendOnePing(new_socket, destinationAddress, ID)
# 3. Call receiveOnePing function
(recv_time, reply_len, reply_size) = self.receiveOnePing(new_socket, destinationAddress, ID, 1)
# 4. Close ICMP socket
new_socket.close()
# 5. Return total network delay
send_time_ms = sendTime * 1000
recv_time_ms = recv_time * 1000
total_delay = recv_time_ms - send_time_ms
return (total_delay, reply_len, reply_size)
def __init__(self, args):
print('Ping to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
# # 2. Call doOnePing function, approximately every second
try:
destinationAddress = socket.gethostbyname(args.hostname)
while True:
(total_delay, reply_len, reply_size) = self.doOnePing(destinationAddress, 1)
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
self.printOneResult(destinationAddress, reply_len, total_delay, reply_size, args.hostname)
time.sleep(1)
# 4. Continue this process until stopped
except BaseException:
print("Host name not recognised")
class Traceroute(NetworkApplication):
# Task 1.2: Traceroute
def __init__(self, args):
# Please ensure you print each result using the printOneResult method!
print('Traceroute to: %s...' % (args.hostname))
# Get IP of destination
dest_address = socket.gethostbyname(args.hostname)
# init ttl_count_up
ttl_count_up = 1
while True:
# Creates sockets
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
# COnstruct and send packet
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, 5, 1)
data = "Task 1.2: Traceroute".encode()
# NOTE: the direction constrainted by checksum generating function, so the above BBHHH will not be modified
new_checksum = self.checksum(header + data)
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, 5, 1)
packet = header + data
send_socket.sendto(packet, (dest_address, 1024 * 10))
send_time = time.time() # Record beginning time
# Loop until packet received
run = True
while run:
recv_packet, address = recv_socket.recvfrom(1024 * 4)
address = address[0]
run = False
send_socket.close()
recv_socket.close()
recv_time = time.time()
# try best to resolv hostname
try:
hostname = socket.gethostbyaddr(address)[0]
except BaseException:
hostname = address
self.printOneResult(address, sys.getsizeof(packet), (recv_time - send_time) * 1000, ttl_count_up, hostname)
ttl_count_up += 1
# dest reach, exit loop
if address == dest_address:
break
class ParisTraceroute(NetworkApplication):
# Task 1.3: Paris-Traceroute
# A well-known limitation of trace route is that it may indicate a path that does not actually
# exist in the presence of “load-balancers” in the network. Consider the example below where
# a source host Src sends traceroute traffic to a destination host Dst.
def getIdentifier(self, checkSumWanted):
return 0xf7ff - checkSumWanted
def __init__(self, args):
try:
print('Paris-Traceroute to: %s...' % (args.hostname))
# Get IP of destination
dest_ip = socket.gethostbyname(args.hostname)
ttl_count_up = 1
# in paris-traceroute, use checksum as identifier
check_sum_count_up = 1
while True:
# Creates sockets
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
send_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
# Limit ttl of socket
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
# COnstruct and send packet
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
header = struct.pack('!BBHHH', ICMP_TYPE, 0, 0, 0, 0)
# NOTE: the checksum acts as a identifier, get the apporiate identifier to get the wanted checksum
# BE(big endian) used here
# checked with opensource from https://paris-traceroute.net
new_identifier = self.getIdentifier(check_sum_count_up)
header = struct.pack('!BBHHH', ICMP_TYPE, 0, check_sum_count_up, new_identifier, 0)
packet = header
send_socket.sendto(packet, (dest_ip, OUTGOING_BUFFER))
# jot down start time for diff
start_time = time.time()
# Loop until packet received
run = True
while run:
recv_packet, address = recv_socket.recvfrom(INCOMING_BUFFER)
address = address[0]
run = False
# close socket after done
send_socket.close()
recv_socket.close()
recv_time = time.time()
# try best to resolv hostname
try:
try_res_hostname = socket.gethostbyaddr(address)[0]
except BaseException:
# bypass if cannot resolv hostname
try_res_hostname = address
self.printOneResult(address, sys.getsizeof(packet), (recv_time - start_time) * 1000, ttl_count_up, try_res_hostname)
ttl_count_up += 1
check_sum_count_up += 1
# dest reach, exit loop
if address == dest_ip:
break
except BaseException as err:
print('error occured', err)
sys.exit(1)
class WebServer(NetworkApplication):
def handleRequest(self, tcpSocket):
# 1. Receive request message from the client on connection socket
getrequest = tcpSocket.recv(INCOMING_BUFFER).decode()
print(getrequest)
# 2. Extract the path of the requested object from the message (second
# part of the HTTP header)
headers = getrequest.split('\n')
filename = headers[0].split()[1]
try:
# 3. Read the corresponding file from disk
filetosend = open(filename.replace('/', ''))
content = filetosend.read()
filetosend.close()
# 4. Store in temporary buffer
response = 'HTTP/1.0 200 OK\n\n' + content
# 5. Send the correct HTTP response error
except FileNotFoundError:
response = 'HTTP/1.0 404 NOT FOUND\n\nFile Not Found'
# 6. Send the content of the file to the socket
tcpSocket.sendall(response.encode())
# 7. Close the connection socket
tcpSocket.close()
pass
def __init__(self, args):
print('Web Server starting on port: %i...' % (args.port))
# 1. Create server socket
server_socket = socket.socket()
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. Bind the server socket to server address and server port
server_socket.bind(("127.0.0.1", args.port))
# 3. Continuously listen for connections to server socket
server_socket.listen()
# 4. When a connection is accepted, call handleRequest function,
# passing new connection socket
# (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
run = True
while run:
client_socket, client_address = server_socket.accept()
self.handleRequest(client_socket)
# 5. Close server socket
server_socket.close()
run = False
class Proxy(NetworkApplication):
# Task 2.2: Web Proxy
# As with Task 2.1, there are a number of ways to test your Web Proxy. For example, to
# generate requests using curl, we can use the following:
# curl neverssl.com - -proxy 127.0.0.1: 8000
# This assumes that the Web Proxy is running on the local machine and bound to port 8000.
# In this case, the URL requested from the proxy is neverssl.com.
def handleRequest(self, tcp_socket):
dst_host = ''
# receive request from client
full_req = tcp_socket.recv(INCOMING_BUFFER).decode('utf-8')
# print("Full req =", full_req)
first_line = full_req.split('\r\n')[0]
[http_action, full_url, http_ver] = first_line.split(' ')
sainted_url = full_url.split('://')[1].replace('/', '')
try_split_port = sainted_url.split(':')
if (len(try_split_port) > 1):
dst_host, dst_port = try_split_port
else:
dst_host = sainted_url
dst_port = 80
try:
# try convert to ip, if not emit gaierror
dst_ip = socket.gethostbyname(dst_host)
# create new socket for sending request
outgoing_req_socket = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
outgoing_req_socket.settimeout(2)
# connect to dst server
outgoing_req_socket.connect((dst_ip, dst_port))
# forward request getting from proxy
outgoing_req_socket.send(full_req.encode('utf-8'))
# print("forwarded the request")
# receive data from the server
while True:
reply = outgoing_req_socket.recv(INCOMING_BUFFER)
if len(reply) > 0:
# forward reply to originator
tcp_socket.send(reply)
else:
# buffer empty, forward reply done
break
# close port
outgoing_req_socket.close()
# handle cannot convert hostname to ip
except socket.gaierror as msg:
print("Couldn't convert domain to ip", dst_host, msg)
if tcp_socket:
tcp_socket.close()
sys.exit(1)
# handle socket timeout
except socket.timeout:
print("Connection timeout")
if outgoing_req_socket:
outgoing_req_socket.close()
return
# final overflow for any error
except socket.error as msg:
print("Socket error:", msg)
if outgoing_req_socket:
outgoing_req_socket.close()
if tcp_socket:
tcp_socket.close()
sys.exit(1)
def __init__(self, args):
server_ip = '127.0.0.1'
server_port = args.port
print('Task 2.2: Web Proxy, starting on port: %i...' % (server_port))
# 1. Create server socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. Bind the server socket to server address and server port
server_socket.bind((server_ip, server_port))
# 3. Continuously listen for connections to server socket
server_socket.listen(1)
serving = True
try:
while serving:
# 4. When a connection is accepted,
# -> call handleIncomingRequest function,
# -> passing new connection socket
# (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
connection, address = server_socket.accept()
self.handleRequest(connection)
except socket.error as msg:
if server_socket:
server_socket.close()
print("Socket error:", msg)
sys.exit(1)
finally:
# 5. Close server socket
if server_socket:
server_socket.close()
sys.exit(0)
if __name__ == "__main__":
args = setupArgumentParser()
args.func(args)

View File

@@ -0,0 +1,587 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import argparse
import socket
import os
import sys
import struct
import time
import random
import traceback # useful for exception handling
import threading
from pprint import pprint
# config
INCOMING_BUFFER = 1024
OUTGOING_BUFFER = INCOMING_BUFFER * 10
ICMP_TYPE = 8
def setupArgumentParser() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description='A collection of Network Applications developed for SCC.203.')
parser.set_defaults(func=ICMPPing, hostname='lancaster.ac.uk')
subparsers = parser.add_subparsers(help='sub-command help')
parser_p = subparsers.add_parser('ping', aliases=['p'], help='run ping')
parser_p.set_defaults(timeout=4)
parser_p.add_argument('hostname', type=str, help='host to ping towards')
parser_p.add_argument(
'--count',
'-c',
nargs='?',
type=int,
help='number of times to ping the host before stopping')
parser_p.add_argument(
'--timeout',
'-t',
nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_p.set_defaults(func=ICMPPing)
parser_t = subparsers.add_parser('traceroute', aliases=['t'],
help='run traceroute')
parser_t.set_defaults(timeout=4, protocol='icmp')
parser_t.add_argument(
'hostname',
type=str,
help='host to traceroute towards')
parser_t.add_argument(
'--timeout',
'-t',
nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_t.add_argument('--protocol', '-p', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_t.set_defaults(func=Traceroute)
parser_pt = subparsers.add_parser('paris-traceroute', aliases=['pt'],
help='run paris-traceroute')
parser_pt.set_defaults(timeout=4, protocol='icmp')
parser_pt.add_argument(
'hostname',
type=str,
help='host to traceroute towards')
parser_pt.add_argument(
'--timeout',
'-t',
nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_pt.add_argument('--protocol', '-p', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_pt.set_defaults(func=ParisTraceroute)
parser_w = subparsers.add_parser(
'web', aliases=['w'], help='run web server')
parser_w.set_defaults(port=8080)
parser_w.add_argument('--port', '-p', type=int, nargs='?',
help='port number to start web server listening on')
parser_w.set_defaults(func=WebServer)
parser_x = subparsers.add_parser('proxy', aliases=['x'], help='run proxy')
parser_x.set_defaults(port=8000)
parser_x.add_argument('--port', '-p', type=int, nargs='?',
help='port number to start web server listening on')
parser_x.set_defaults(func=Proxy)
args = parser.parse_args()
return args
class NetworkApplication:
def checksum(self, dataToChecksum: str) -> str:
csum = 0
countTo = (len(dataToChecksum) // 2) * 2
count = 0
while count < countTo:
thisVal = dataToChecksum[count + 1] * 256 + dataToChecksum[count]
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2
if countTo < len(dataToChecksum):
csum = csum + dataToChecksum[len(dataToChecksum) - 1]
csum = csum & 0xffffffff
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
answer = socket.htons(answer)
return answer
def printOneResult(
self,
destinationAddress: str,
packetLength: int,
time: float,
ttl: int,
destinationHostname=''):
if destinationHostname:
print(
"%d bytes from %s (%s): ttl=%d time=%.2f ms" %
(packetLength, destinationHostname, destinationAddress, ttl, time))
else:
print("%d bytes from %s: ttl=%d time=%.2f ms" %
(packetLength, destinationAddress, ttl, time))
def printAdditionalDetails(
self,
packetLoss=0.0,
minimumDelay=0.0,
averageDelay=0.0,
maximumDelay=0.0):
print("%.2f%% packet loss" % (packetLoss))
if minimumDelay > 0 and averageDelay > 0 and maximumDelay > 0:
print("rtt min/avg/max = %.2f/%.2f/%.2f ms" %
(minimumDelay, averageDelay, maximumDelay))
def printMultipleResults(
self,
ttl: int,
destinationAddress: str,
measurements: list,
destinationHostname=''):
latencies = ''
noResponse = True
for rtt in measurements:
if rtt is not None:
latencies += str(round(rtt, 3))
latencies += ' ms '
noResponse = False
else:
latencies += '* '
if noResponse is False:
print(
"%d %s (%s) %s" %
(ttl,
destinationHostname,
destinationAddress,
latencies))
else:
print("%d %s" % (ttl, latencies))
class ICMPPing(NetworkApplication):
# Task 1.1: ICMP Ping
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
# 1. Wait for the socket to receive a reply
icmpSocket.settimeout(timeout)
try:
reply, addr = icmpSocket.recvfrom(2048)
except socket.timeout as msg:
print("No data received from socket within timeout period. Message: " + str(msg))
sys.exit(1)
# 2. Once received, record time of receipt, otherwise, handle a timeout
recv_time = time.time()
# 4. Unpack the packet header for useful information, including the ID
# icmp header of the received packet,
# bottom of packet because of network byte-order
# align offset to include the layer 2 encap = 14
reply_size = struct.unpack(">H", reply[16 - 14:18 - 14])[0]
reply_ttl = struct.unpack(">B", reply[22 - 14:23 - 14])[0]
reply_id = struct.unpack(">B", reply[38 - 14:39 - 14])[0]
# 5. Check that the ID matches between the request and reply
if reply_id != ID:
print("Received packet ID not match")
sys.exit(1)
# 6. Return recv time + packet size
return (recv_time, reply_size, reply_ttl)
def sendOnePing(self, icmpSocket, destinationAddress, ID):
# 1. Build ICMP header
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, ID, 1)
data = bytes("Task 1.1: ICMP Ping", 'utf-8')
# 2. Checksum ICMP packet using given function
new_checksum = self.checksum(header + data)
# 3. Insert checksum into packet
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, ID, 1)
packet = header + data
# 4. Send packet using socket
while packet:
sent = icmpSocket.sendto(packet, (destinationAddress, 1500)) # 1500 = Port number
packet = packet[sent:]
# 5. Record time of sending
sendTime = time.time()
return sendTime
def doOnePing(self, destinationAddress, timeout):
# 1. Create ICMP socket
# Sends raw packets to ipv4 addresses
new_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))
ID = 1
# 2. Call sendOnePing function
sendTime = self.sendOnePing(new_socket, destinationAddress, ID)
# 3. Call receiveOnePing function
(recv_time, reply_len, reply_size) = self.receiveOnePing(new_socket, destinationAddress, ID, 1)
# 4. Close ICMP socket
new_socket.close()
# 5. Return total network delay
send_time_ms = sendTime * 1000
recv_time_ms = recv_time * 1000
total_delay = recv_time_ms - send_time_ms
return (total_delay, reply_len, reply_size)
def __init__(self, args):
print('Ping to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
# # 2. Call doOnePing function, approximately every second
try:
destinationAddress = socket.gethostbyname(args.hostname)
while True:
(total_delay, reply_len, reply_size) = self.doOnePing(destinationAddress, 1)
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
self.printOneResult(destinationAddress, reply_len, total_delay, reply_size, args.hostname)
time.sleep(1)
# 4. Continue this process until stopped
except BaseException:
print("Host name not recognised")
class Traceroute(NetworkApplication):
# Task 1.2: Traceroute
def __init__(self, args):
# Please ensure you print each result using the printOneResult method!
print('Traceroute to: %s...' % (args.hostname))
# Get IP of destination
dest_address = socket.gethostbyname(args.hostname)
# init ttl_count_up
ttl_count_up = 1
while True:
# Creates sockets
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
# COnstruct and send packet
header = struct.pack('BBHHH', ICMP_TYPE, 0, 0, 5, 1)
data = "Task 1.2: Traceroute".encode()
# NOTE: the direction constrainted by checksum generating function, so the above BBHHH will not be modified
new_checksum = self.checksum(header + data)
header = struct.pack('BBHHH', ICMP_TYPE, 0, new_checksum, 5, 1)
packet = header + data
send_socket.sendto(packet, (dest_address, 1024 * 10))
send_time = time.time() # Record beginning time
# Loop until packet received
run = True
while run:
recv_packet, address = recv_socket.recvfrom(1024 * 4)
address = address[0]
run = False
send_socket.close()
recv_socket.close()
recv_time = time.time()
# try best to resolv hostname
try:
hostname = socket.gethostbyaddr(address)[0]
except BaseException:
hostname = address
self.printOneResult(address, sys.getsizeof(packet), (recv_time - send_time) * 1000, ttl_count_up, hostname)
ttl_count_up += 1
# dest reach, exit loop
if address == dest_address:
break
class ParisTraceroute(NetworkApplication):
# Task 1.3: Paris-Traceroute
# A well-known limitation of trace route is that it may indicate a path that does not actually
# exist in the presence of “load-balancers” in the network. Consider the example below where
# a source host Src sends traceroute traffic to a destination host Dst.
def getIdentifier(self, checkSumWanted):
return 0xf7ff - checkSumWanted
def __init__(self, args):
try:
print('Paris-Traceroute to: %s...' % (args.hostname))
# Get IP of destination
dest_ip = socket.gethostbyname(args.hostname)
ttl_count_up = 1
# in paris-traceroute, use checksum as identifier
check_sum_count_up = 1
while True:
# Creates sockets
recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
send_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
# Limit ttl of socket
send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl_count_up)
# COnstruct and send packet
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
header = struct.pack('!BBHHH', ICMP_TYPE, 0, 0, 0, 0)
# NOTE: the checksum acts as a identifier, get the apporiate identifier to get the wanted checksum
# BE(big endian) used here
# checked with opensource from https://paris-traceroute.net
new_identifier = self.getIdentifier(check_sum_count_up)
header = struct.pack('!BBHHH', ICMP_TYPE, 0, check_sum_count_up, new_identifier, 0)
packet = header
send_socket.sendto(packet, (dest_ip, OUTGOING_BUFFER))
# jot down start time for diff
start_time = time.time()
# Loop until packet received
run = True
while run:
recv_packet, address = recv_socket.recvfrom(INCOMING_BUFFER)
address = address[0]
run = False
# close socket after done
send_socket.close()
recv_socket.close()
recv_time = time.time()
# try best to resolv hostname
try:
try_res_hostname = socket.gethostbyaddr(address)[0]
except BaseException:
# bypass if cannot resolv hostname
try_res_hostname = address
self.printOneResult(address, sys.getsizeof(packet), (recv_time - start_time) * 1000, ttl_count_up, try_res_hostname)
ttl_count_up += 1
check_sum_count_up += 1
# dest reach, exit loop
if address == dest_ip:
break
except BaseException as err:
print('error occured', err)
sys.exit(1)
class WebServer(NetworkApplication):
def handleRequest(self, tcpSocket):
# 1. Receive request message from the client on connection socket
getrequest = tcpSocket.recv(INCOMING_BUFFER).decode()
print(getrequest)
# 2. Extract the path of the requested object from the message (second
# part of the HTTP header)
headers = getrequest.split('\n')
filename = headers[0].split()[1]
try:
# 3. Read the corresponding file from disk
filetosend = open(filename.replace('/', ''))
content = filetosend.read()
filetosend.close()
# 4. Store in temporary buffer
response = 'HTTP/1.0 200 OK\n\n' + content
# 5. Send the correct HTTP response error
except FileNotFoundError:
response = 'HTTP/1.0 404 NOT FOUND\n\nFile Not Found'
# 6. Send the content of the file to the socket
tcpSocket.sendall(response.encode())
# 7. Close the connection socket
tcpSocket.close()
pass
def __init__(self, args):
print('Web Server starting on port: %i...' % (args.port))
# 1. Create server socket
server_socket = socket.socket()
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 2. Bind the server socket to server address and server port
server_socket.bind(("127.0.0.1", args.port))
# 3. Continuously listen for connections to server socket
server_socket.listen()
# 4. When a connection is accepted, call handleRequest function,
# passing new connection socket
# (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
run = True
while run:
client_socket, client_address = server_socket.accept()
self.handleRequest(client_socket)
# 5. Close server socket
server_socket.close()
run = False
class Proxy(NetworkApplication):
# Task 2.2: Web Proxy
# As with Task 2.1, there are a number of ways to test your Web Proxy. For example, to
# generate requests using curl, we can use the following:
# curl neverssl.com - -proxy 127.0.0.1: 8000
# This assumes that the Web Proxy is running on the local machine and bound to port 8000.
# In this case, the URL requested from the proxy is neverssl.com.
def handleRequest(self, tcp_socket):
dst_host = ''
# receive request from client
full_req = tcp_socket.recv(INCOMING_BUFFER).decode('utf-8')
# print("Full req =", full_req)
first_line = full_req.split('\r\n')[0]
[http_action, full_url, http_ver] = first_line.split(' ')
sainted_url = full_url.split('://')[1].replace('/', '')
try_split_port = sainted_url.split(':')
if (len(try_split_port) > 1):
dst_host, dst_port = try_split_port
else:
dst_host = sainted_url
dst_port = 80
try:
# try convert to ip, if not emit gaierror
dst_ip = socket.gethostbyname(dst_host)
# create new socket for sending request
outgoing_req_socket = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
outgoing_req_socket.settimeout(2)
# connect to dst server
outgoing_req_socket.connect((dst_ip, dst_port))
# forward request getting from proxy
outgoing_req_socket.send(full_req.encode('utf-8'))
# print("forwarded the request")
# receive data from the server
while True:
reply = outgoing_req_socket.recv(INCOMING_BUFFER)
if len(reply) > 0:
# forward reply to originator
tcp_socket.send(reply)
else:
# buffer empty, forward reply done
break
# close port
outgoing_req_socket.close()
# handle cannot convert hostname to ip
except socket.gaierror as msg:
print("Couldn't convert domain to ip", dst_host, msg)
if tcp_socket:
tcp_socket.close()
sys.exit(1)
# handle socket timeout
except socket.timeout:
print("Connection timeout")
if outgoing_req_socket:
outgoing_req_socket.close()
return
# final overflow for any error
except socket.error as msg:
print("Socket error:", msg)
if outgoing_req_socket:
outgoing_req_socket.close()
if tcp_socket:
tcp_socket.close()
sys.exit(1)
def __init__(self, args):
server_ip = '127.0.0.1'
server_port = args.port
print('Task 2.2: Web Proxy, starting on port: %i...' % (server_port))
# 1. Create server socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. Bind the server socket to server address and server port
server_socket.bind((server_ip, server_port))
# 3. Continuously listen for connections to server socket
server_socket.listen(1)
serving = True
try:
while serving:
# 4. When a connection is accepted,
# -> call handleIncomingRequest function,
# -> passing new connection socket
# (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
connection, address = server_socket.accept()
self.handleRequest(connection)
except socket.error as msg:
if server_socket:
server_socket.close()
print("Socket error:", msg)
sys.exit(1)
finally:
# 5. Close server socket
if server_socket:
server_socket.close()
sys.exit(0)
if __name__ == "__main__":
args = setupArgumentParser()
args.func(args)

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import argparse
import socket
import os
import sys
import struct
import time
import random
import traceback # useful for exception handling
import threading
def setupArgumentParser() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description='A collection of Network Applications developed for SCC.203.')
parser.set_defaults(func=ICMPPing, hostname='lancaster.ac.uk')
subparsers = parser.add_subparsers(help='sub-command help')
parser_p = subparsers.add_parser('ping', aliases=['p'], help='run ping')
parser_p.set_defaults(timeout=4)
parser_p.add_argument('hostname', type=str, help='host to ping towards')
parser_p.add_argument('--count', '-c', nargs='?', type=int,
help='number of times to ping the host before stopping')
parser_p.add_argument('--timeout', '-t', nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_p.set_defaults(func=ICMPPing)
parser_t = subparsers.add_parser('traceroute', aliases=['t'],
help='run traceroute')
parser_t.set_defaults(timeout=4, protocol='icmp')
parser_t.add_argument('hostname', type=str, help='host to traceroute towards')
parser_t.add_argument('--timeout', '-t', nargs='?', type=int,
help='maximum timeout before considering request lost')
parser_t.add_argument('--protocol', '-p', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_t.set_defaults(func=Traceroute)
parser_pt = subparsers.add_parser('paris-traceroute', aliases=['pt'],
help='run paris-traceroute')
parser_pt.set_defaults(timeout=4, protocol='icmp')
parser_pt.add_argument('hostname', type=str, help='host to traceroute towards')
parser_pt.add_argument('--timeout', '-t', nargs='?', type=int,
help='maximum timeout before considering request lost')
parser_pt.add_argument('--protocol', '-p', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_pt.set_defaults(func=ParisTraceroute)
parser_w = subparsers.add_parser('web', aliases=['w'], help='run web server')
parser_w.set_defaults(port=8080)
parser_w.add_argument('--port', '-p', type=int, nargs='?',
help='port number to start web server listening on')
parser_w.set_defaults(func=WebServer)
parser_x = subparsers.add_parser('proxy', aliases=['x'], help='run proxy')
parser_x.set_defaults(port=8000)
parser_x.add_argument('--port', '-p', type=int, nargs='?',
help='port number to start web server listening on')
parser_x.set_defaults(func=Proxy)
args = parser.parse_args()
return args
class NetworkApplication:
def checksum(self, dataToChecksum: str) -> str:
csum = 0
countTo = (len(dataToChecksum) // 2) * 2
count = 0
while count < countTo:
thisVal = dataToChecksum[count+1] * 256 + dataToChecksum[count]
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2
if countTo < len(dataToChecksum):
csum = csum + dataToChecksum[len(dataToChecksum) - 1]
csum = csum & 0xffffffff
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
answer = socket.htons(answer)
return answer
def printOneResult(self, destinationAddress: str, packetLength: int, time: float, ttl: int, destinationHostname=''):
if destinationHostname:
print("%d bytes from %s (%s): ttl=%d time=%.2f ms" % (packetLength, destinationHostname, destinationAddress, ttl, time))
else:
print("%d bytes from %s: ttl=%d time=%.2f ms" % (packetLength, destinationAddress, ttl, time))
def printAdditionalDetails(self, packetLoss=0.0, minimumDelay=0.0, averageDelay=0.0, maximumDelay=0.0):
print("%.2f%% packet loss" % (packetLoss))
if minimumDelay > 0 and averageDelay > 0 and maximumDelay > 0:
print("rtt min/avg/max = %.2f/%.2f/%.2f ms" % (minimumDelay, averageDelay, maximumDelay))
def printMultipleResults(self, ttl: int, destinationAddress: str, measurements: list, destinationHostname=''):
latencies = ''
noResponse = True
for rtt in measurements:
if rtt is not None:
latencies += str(round(rtt, 3))
latencies += ' ms '
noResponse = False
else:
latencies += '* '
if noResponse is False:
print("%d %s (%s) %s" % (ttl, destinationHostname, destinationAddress, latencies))
else:
print("%d %s" % (ttl, latencies))
class ICMPPing(NetworkApplication):
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
# 1. Wait for the socket to receive a reply
# 2. Once received, record time of receipt, otherwise, handle a timeout
# 3. Compare the time of receipt to time of sending, producing the total network delay
# 4. Unpack the packet header for useful information, including the ID
# 5. Check that the ID matches between the request and reply
# 6. Return total network delay
pass
def sendOnePing(self, icmpSocket, destinationAddress, ID):
# 1. Build ICMP header
# 2. Checksum ICMP packet using given function
# 3. Insert checksum into packet
# 4. Send packet using socket
# 5. Record time of sending
pass
def doOnePing(self, destinationAddress, timeout):
# 1. Create ICMP socket
# 2. Call sendOnePing function
# 3. Call receiveOnePing function
# 4. Close ICMP socket
# 5. Return total network delay
pass
def __init__(self, args):
print('Ping to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
# 2. Call doOnePing function, approximately every second
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
self.printOneResult('1.1.1.1', 50, 20.0, 150) # Example use of printOneResult - complete as appropriate
# 4. Continue this process until stopped
class Traceroute(NetworkApplication):
def __init__(self, args):
# Please ensure you print each result using the printOneResult method!
print('Traceroute to: %s...' % (args.hostname))
class ParisTraceroute(NetworkApplication):
def __init__(self, args):
# Please ensure you print each result using the printOneResult method!
print('Paris-Traceroute to: %s...' % (args.hostname))
class WebServer(NetworkApplication):
def handleRequest(tcpSocket):
# 1. Receive request message from the client on connection socket
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
# 3. Read the corresponding file from disk
# 4. Store in temporary buffer
# 5. Send the correct HTTP response error
# 6. Send the content of the file to the socket
# 7. Close the connection socket
pass
def __init__(self, args):
print('Web Server starting on port: %i...' % (args.port))
# 1. Create server socket
# 2. Bind the server socket to server address and server port
# 3. Continuously listen for connections to server socket
# 4. When a connection is accepted, call handleRequest function, passing new connection socket (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
# 5. Close server socket
class Proxy(NetworkApplication):
def __init__(self, args):
print('Web Proxy starting on port: %i...' % (args.port))
if __name__ == "__main__":
args = setupArgumentParser()
args.func(args)

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
rm -rf _temp/*
rm -rf delivery.zip
mkdir -p _temp
set -ex
cp NetworkApplications.py _temp/NetworkApplications.py
cp test.sh _temp/test.sh
pushd _temp
7za a -tzip ../delivery.zip *
popd
rm -rf _temp

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -ex
autopep8 --max-line-length 160 --in-place --aggressive --aggressive NetworkApplications.py

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
helloworld
</body>
</html>

View File

@@ -0,0 +1,5 @@
https://www.ietf.org/rfc/rfc792.txt
https://realpython.com/python-sockets/
https://lwn.net/Articles/422330/
https://fasionchan.com/network/icmp/ping-py/

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -ex
# python ./NetworkApplications.py -h
# test web
# python ./NetworkApplications.py w
# curl http://localhost:8080/index.html
# test ParisTraceroute
# sudo su
# python ./NetworkApplications.py pt www.google.com
# test ping
# python ./NetworkApplications.py p www.google.com
# test traceroute
# traceroute -I www.google.com
# paris-traceroute -I www.google.com
# python ./NetworkApplications.py t www.google.com
# test proxy
# python ./NetworkApplications.py x
# curl neverssl.com --proxy 127.0.0.1:8000
# curl http://neverssl.com --proxy 127.0.0.1:8000
# curl https://neverssl.com:443 --proxy 127.0.0.1:8000
# utils
# gethostbyname
# python ./NetworkApplications.py p localhost

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -ex
curl http://neverssl.com:80 --proxy 127.0.0.1:8000
# curl http://neverssl.com --proxy 127.0.0.1:8000
# curl http://neverssl.com:8080 --proxy 127.0.0.1:8000
# curl neverssl.com --proxy 127.0.0.1:8000

View File

@@ -0,0 +1,409 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
######
import argparse
import socket
import os
import sys
import struct
import time
def setupArgumentParser() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description='A collection of Network Applications developed for SCC.203.')
parser.set_defaults(func=ICMPPing, hostname='lancaster.ac.uk')
subparsers = parser.add_subparsers(help='sub-command help')
parser_p = subparsers.add_parser(
'ping', aliases=['p'], help='run ping')
parser_p.add_argument('hostname', type=str,
help='host to ping towards')
parser_p.add_argument('count', nargs='?', type=int,
help='number of times to ping the host before stopping')
parser_p.add_argument('timeout', nargs='?',
type=int,
help='maximum timeout before considering request lost')
parser_p.set_defaults(func=ICMPPing)
parser_t = subparsers.add_parser('traceroute', aliases=['t'],
help='run traceroute')
parser_t.add_argument('hostname', type=str,
help='host to traceroute towards')
parser_t.add_argument('timeout', nargs='?', type=int,
help='maximum timeout before considering request lost')
parser_t.add_argument('protocol', nargs='?', type=str,
help='protocol to send request with (UDP/ICMP)')
parser_t.set_defaults(func=Traceroute)
parser_w = subparsers.add_parser(
'web', aliases=['w'], help='run web server')
parser_w.set_defaults(port=8080)
parser_w.add_argument('port', type=int, nargs='?',
help='port number to start web server listening on')
parser_w.set_defaults(func=WebServer)
parser_x = subparsers.add_parser(
'proxy', aliases=['x'], help='run proxy')
parser_x.set_defaults(port=8000)
parser_x.add_argument('port', type=int, nargs='?',
help='port number to start web server listening on')
parser_x.set_defaults(func=Proxy)
args = parser.parse_args()
return args
class NetworkApplication:
def checksum(self, dataToChecksum: str) -> str:
csum = 0
countTo = (len(dataToChecksum) // 2) * 2
count = 0
while count < countTo:
thisVal = dataToChecksum[count+1] * 256 + dataToChecksum[count]
csum = csum + thisVal
csum = csum & 0xffffffff
count = count + 2
if countTo < len(dataToChecksum):
csum = csum + dataToChecksum[len(dataToChecksum) - 1]
csum = csum & 0xffffffff
csum = (csum >> 16) + (csum & 0xffff)
csum = csum + (csum >> 16)
answer = ~csum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
answer = socket.htons(answer)
return answer
def printOneResult(self, destinationAddress: str, packetLength: int, time: float, ttl: int, destinationHostname=''):
if destinationHostname:
print("%d bytes from %s (%s):ttl=%d time=%.2f ms" % (
packetLength, destinationHostname, destinationAddress, ttl, time))
else:
print("%d bytes from %s: ttl=%dtime=%.2f ms" %
(packetLength, destinationAddress, ttl, time))
def printAdditionalDetails(self, packetLoss=0.0, minimumDelay=0.0, averageDelay=0.0, maximumDelay=0.0):
print("%.2f%% packet loss" % (packetLoss))
if minimumDelay > 0 and averageDelay > 0 and maximumDelay > 0:
print("rtt min/avg/max = %.2f/%.2f/%.2fms" %
(minimumDelay, averageDelay, maximumDelay))
class ICMPPing(NetworkApplication):
def receiveOnePing(self, icmpSocket, destinationAddress, ID, timeout):
# 1. Wait for the socket to receive a reply. #2. Once received, record time of receipt, otherwise, handle a timeout
try:
timeRecieved = time.time()
information, address = icmpSocket.recvfrom(1024)
timeSent = information.split()[2]
# 3. Compare the time of receipt to time of sending, producing the total network delay
timeSent= self.sendOnePing(icmpSocket, destinationAddress, 111)
totalNetworkDelay = (timeRecieved*1000) - timeSent
# 4. Unpack the packet header for useful information, including the ID
icmpType, icmpCode, icmpChecksum, icmpPacketID, icmpSeqNumber = struct.unpack("bbHHh", icmpHeader)
# 5. Check that the ID matches between the request and reply AND THEN 6. Return total network delay
if(icmpPacketID == self.ID):
return totalNetworkDelay
else:
return 0
except timeout: # No response received, print the timeout message
print("Request timed out")
def sendOnePing(self, icmpSocket, destinationAddress, ID):
# 1. Build ICMP header
icmpHeader=struct.pack("bbHHh", 8, 0, 0, ID, 1)
# 2. Checksum ICMP packet using given function
icmpChecksum = self.checksum(icmpHeader)
# 3. Insert checksum into packet
icmpHeader = struct.pack("bbHHh", 8, 0, icmpChecksum, ID, 1)
# 4. Send packet using socket- double check this //run with wireshark
icmpSocket.sendto(icmpHeader, (destinationAddress, 1))
# 5. Record time of sending
timeSent=time.time()
return timeSent
def doOnePing(self, destinationAddress, timeout):
# 1. Create ICMP socket
# Translate an Internet protocol name (for example, 'icmp') to a constant suitable for passing as the (optional) third argument to the socket() function.
icmp_proto = socket.getprotobyname("icmp") #debugging
icmpSocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_proto)
#icmpSocket = socket.socket(socket.AF_INET,socket.SOCK_RAW, socket.IPPROTO_ICMP)
# 2. Call sendOnePing function
timeSent = self.sendOnePing(icmpSocket, destinationAddress, 111)
# 3. Call receiveOnePing function
networkDelay = self.receiveOnePing(icmpSocket, destinationAddress, 111, 1000, timeSent)
# 4. Close ICMP socket
icmpSocket.close()
# 5. Return total network delay
return networkDelay
def __init__(self, args):
print('Ping to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
ipAddress = socket.gethostbyname(args.hostname)
# 2. Call doOnePing function approximately every second
while True:
time.sleep(1)
debuggingTimeout = args.timeout
print("testing:", ipAddress, debuggingTimeout)
returnedDelay = self.doOnePing(ipAddress, debuggingTimeout)
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
self.printOneResult(ipAddress, 50, returnedDelay, 150)
#Example use of printOneResult - complete as appropriate
# 4. Continue this process until stopped - did this through the while True
class Traceroute(NetworkApplication):
def __init__(self, args):
#
# Please ensure you print each result using the printOneResult method!
print('Traceroute to: %s...' % (args.hostname))
# 1. Look up hostname, resolving it to an IP address
ipAddress= socket.gethostbyname(args.hostname)
numberofNodes= 0 # create variable and initialise
# 2. Call PingOneNode function approximately every second
while True:
time.sleep(1)
#nodalDelay = self.pingOneNode(ipAddress, args.timeout, 1)
nodalDelay = self.pingOneNode()
self.printOneResult(ipAddress, 50, nodalDelay[1]*1000, 150)
numberofNodes = numberofNodes + 1 # increments number of nodes
# 4. Continue this process until stopped - until ICMP = 0
if self.ICMP_CODE == 0:
break
# 3. Print out the returned delay (and other relevant details) using the printOneResult method
# check this don't think its right
self.printOneResult(ipAddress, 50, nodalDelay[1]*1000, 150)
def pingOneNode(self):
# 1. Create ICMP socket
icmp_proto = socket.getprotobyname("icmp") #debugging
icmpSocket= socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_proto)
# 2. Call sendNodePing function
timeSent= self.sendNodePing(icmpSocket, self.ipAddress, 111)
# 3. Call recieveNodePing function
networkDelay= self.recieveNodePing(icmpSocket, self.ipAddress, 111, 1000, timeSent)
# 4. Close ICMP socket
icmpSocket.close()
# 5. Return total network delay- add up all the nodes
x = 0
for x in self.numberOfNodes:
totalDelay = (networkDelay[x] + networkDelay[x + 1])
x = x + 1
if x == self.numberOfNodes:
break
return totalDelay
def sendNodePing(icmpSocket):
# 1. Build ICMP header
icmpHeader= struct.pack("bbHHh", 8, 0, 0, ID, 1)
# 2. Checksum ICMP packet using given function
icmpChecksum= self.checksum(icmpHeader)
# 3. Insert checksum into packet
packetHeader= struct.pack("bbHHh", 8, 0, icmpChecksum, ID, 1)
packet= packetHeader
# 4. Send packet using socket
# double check this //run with wireshark
icmpSocket.sendto(packet, (self.icmpAddress, 1))
# 5. Record time of sending
sentTime= time.time()
return sentTime
def recieveNodePing(icmpSocket):
# 1. Wait for the socket to receive a reply- TTL = 0
sentTime= time.time()
## Set the TTL for messages to 1 so they do not go past the local network segment
#TTL = socket.recvmessage()
TTL = struct.pack('b', 1)
icmpSocket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, TTL)
# 2. Once received, record time of receipt, otherwise, handle a timeout
try: # TTL == 0
timeRecieved = time.time()
# 3. Compare the time of receipt to time of sending, producing the total network delay- did when calculated RTT?
totalNetworkDelay = (timeRecieved * 1000) - sentTime
# 4. Unpack the packet header for useful information, including the ID
icmpType, icmpCode, icmpChecksum, icmpPacketID, icmpSeqNumber= struct.unpack("bbHHh", icmpHeader)
# 5. Check that the ID matches between the request and reply and # 6. Return total network delay
if(icmpPacketID == self.ID):
return totalNetworkDelay
else:
return 0
except TTL != 0: #if nothing is recieved, handle a timeout
print("TTL is 0 - socket has not recieved a reply")
return None
class WebServer(NetworkApplication):
def handleRequest(tcpSocket):
# 1. Receive request message from the client on connection (tcp?) socket
tcpSocket = serverSocket.accept() # acceptrequest
bufferSize = tcpSocket.CMSG_SPACE(4) # IPv4 address is 4 bytes in length - calculates the size of the buffer that should be allocated for receiving the ancillary data.
#recieve message in buffer size allocated
requestMessage = tcpSocket.recvmsg(bufferSize[0, [0]])
# 2. Extract the path of the requested object from the message (second part of the HTTP header)
file = requestMessage.unpack_from(bufferSize) # returns a tuple
# 3. Read the corresponding file from disk
socket.sendfile(file)
# 4. Store in temporary buffer
tempBuffer = socket.makefile( mode = 'r', buffering =None, encoding=None, errors=None, newline=None)
tempFile = struct.pack_into(format, self.tempBuffer, 0, file)
# 5. Send the correct HTTP response error
httpResponseError= ("HTTP/1.1 404 Not Found\r\n")
tcpSocket.sendmsg(httpResponseError)
# 6. Send the content of the file to the socket
tcpSocket.recvmsg(bufferSize[0, 0])
# 7. Close the connection socket
tcpSocket.close()
pass
def __init__(self, args):
print('Web Server starting on port: %i...' % (args.port))
# 1. Create server socket
serverSocket= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("creating server socket")
# 2. Bind the server socket to server address and server port
#serverSocket.bind((socket.gethostname(), 80))
serverSocket.bind((sys.argv[1],80))
print("binding socket")
# 3. Continuously listen for connections to server socket
serverSocket.listen(5)
# 4. When a connection is accepted, call handleRequest function, passing new connection socket (see https://docs.python.org/3/library/socket.html#socket.socket.accept)
newSocket= socket.accept()
while True:
handleRequest(newSocket)
print("calling handleRequest")
# 5. Close server socket
serverSocket.close()
class Proxy(NetworkApplication):
def __init__(self, args):
print('Web Proxy starting on port: %i...' % (args.port))
#if __name__ == "__main__":
# args=setupArgumentParser()
# args.func(args)
#1. create server socket and listen - connectionless socket: used to establish a TCP connection with the HTTP server
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#2. Bind the server socket to server address and server port
serverSocket.bind((socket.gethostname(), 80))
#serverSocket.bind(('', args.port))
#serverSocket.bind((sys.argv[1],80))
print("binding socket")
serverSocket.listen(5)
#3. create proxy
proxySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxySocket.bind((socket.gethostname(), args.port))
# become a server socket
proxySocket.listen(5)
#4. Continuously listen for connections to server socket and proxy
#5. When a connection is accepted, call handleRequest function, passing new connection socket (?)
while 1:
connectionSocket, addr = serverSocket.accept() # accept TCP connection from client
with serverSocket.accept()[0] as connectionSocket: #pass new connection socket
print("recieved connection from ", addr)
handleRequest(proxySocket)
print("calling handleRequest")
# 5. Close server socket?
serverSocket.close()
def handleRequest(connectionSocket):
#1. Receive request message from the client on connection socket
# IPv4 address is 4 bytes in length
bufferSize = connectionSocket.CMSG_SPACE(4)
requestMessage = connectionSocket.recvmsg(bufferSize[0, [0]])
#2. forward to proxy
proxySocket.recvmsg(requestMessage)
#3. proxy extracts the path of the requested object from the message (second part of the HTTP header)
file = requestMessage.unpack_from( format, buffer, offset = 1) # returns a tuple
filename= requestMessage.split()[1]
#4. Read the corresponding file from disk: proxy server checks to see if object is stored locally
try:
fileOpen = open(filename[1:], "r") # open file in text mode
outputdata = fileOpen.readlines()
isObjectLocal == True
# 1. if it does, the proxy server returns the object within a HTTP response message to the client browser
httpResponse= ("GET /" + file + " HTTP/1.1\r\n\r\n")
# 3. Read the corresponding file from disk
socket.sendfile(object, offset = 0, count =None)
#send via HTTP response message to client Browser
except IOError:
if isObjectLocal == False:
# 2. if it doesnt, the proxy server opens a TCP connection to the origin server??
originIP = serverSocket.gethostbyname(args.hostname)
proxySocket.connect(originIP, port)
# proxySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bind the socket to a public host, and a well-known port
# proxySocket.bind((socket.gethostname(), 80))
#sends HTTP request for object
httpRequest= ("GET /" + file + " HTTP/1.1\r\n\r\n")
proxySocket.send(httpRequest.encode())
#origin server recieves request
connectionSocket.recvmessage(httpRequest.encode())
#5. Store in temporary buffer
hostn = filename.split('/')[0].replace("www.","",1)
connectionSocket.connect((hostn,80))
# Create a temporary file on this socket
tempObject = proxySocket.makefile('r', 0)
tempObject.write("GET "+"http://" + filename + " HTTP/1.0\n\n")
#6. Send the correct HTTP response error
httpRequest= ("GET /" + file + " HTTP/1.1\r\n\r\n")
connectionSocket.send(httpRequest.encode())
#connctionSocket.send("HTTP/1.1 200 OK\r\n\r\n")
print("Request message sent")
#7. send content to webserver
object = connectionSocket.send(bufferSize[0, 0])
serverSocket.recvmsg(object)
#8. Send the content of the file to the socket
#9. Close the connection socket
connectionSocket.close()
if __name__ == "__main__":
args= setupArgumentParser()
args.func(args)
def main():
print("running")
NetworkApplication()

Some files were not shown because too many files have changed in this diff Show More