├── .gitignore ├── 1-Simple Client and Server ├── client.py └── server.py ├── 2-A Bit Complicated Client and Server - Data Retrieval ├── geo_client_tcp.py ├── geo_server_tcp.py └── geo_world.txt ├── 3-DNS Resolver ├── README.md ├── clarifications │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ └── 5.jpg ├── dns_query.png ├── dns_query_hex.png ├── resolver.py └── test_resolver.py ├── 4-DNS Server ├── README.md ├── cs430.zone ├── nameserver.md ├── nameserver.py ├── output │ ├── IMG_20181008_120335654.jpg │ ├── IMG_20181008_120343398_HDR.jpg │ ├── IMG_20181008_120349203_HDR.jpg │ └── IMG_20181008_120400036.jpg ├── test_nameserver.py └── zoo.zone ├── 5-Web Server ├── README.md ├── alice30.txt ├── webserver.pcapng ├── webserver.py └── webserver_404.pcapng ├── 6-Ping Tool ├── README.md ├── __init__.py ├── pinger.py └── pinger_output.txt ├── 7-Traceroute Tool ├── README.md ├── Traceroute.java ├── traceroute.cpp ├── traceroute.py ├── traceroute_afrinic.txt ├── traceroute_apnic.txt ├── traceroute_arin.txt ├── traceroute_example.txt ├── traceroute_example_from_luther.txt ├── traceroute_lacnic.txt └── traceroute_ripe.txt ├── 8-Distance Vector Routing Protocol ├── README.md ├── __init__.py ├── network_1_config.toml ├── network_1_config.txt ├── network_1_layout.png ├── network_2_config.toml ├── network_2_config.txt ├── network_2_layout.png ├── router_1.py ├── router_2.py ├── router_3.py ├── router_4.py ├── routing.apng ├── routing_capture.pcapng ├── routingfinal.png └── trivial_routing_protocol.lua ├── 9-Homework Review Questions ├── homework0 │ └── osmanHomework0.txt ├── homework1 │ ├── Question10.png │ ├── Question7.png │ ├── Question9.png │ ├── osmanHomework1.docx │ └── osmanHomework1.pdf ├── homework10 │ ├── homework10.png │ └── osmanHomework10.txt ├── homework2 │ ├── osmanHomework2.docx │ └── osmanHomework2.pdf ├── homework3 │ ├── CS430_DNS.pcapng │ ├── CS430_DNS.zip │ ├── osmanHomework3.docx │ └── osmanHomework3.pdf ├── homework4 │ ├── captured.txt │ └── osmanHomework4.txt ├── homework5 │ ├── CHKSUM │ │ ├── IMG_20181008_112713371.jpg │ │ ├── IMG_20181008_112715917.jpg │ │ └── IMG_20181008_113255589.jpg │ ├── captured.txt │ └── osmanHomework5.txt ├── homework6 │ └── osmanHomework6.txt ├── homework7 │ ├── CS430_TCP.pcap │ └── osmanHomework7.txt ├── homework8 │ ├── osmanHomework8.txt │ ├── tcp_congestion_reno.PNG │ └── telnet_capture_seq_ack.PNG └── homework9 │ └── osmanHomework9.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Created by https://www.gitignore.io/api/python,visualstudiocode 3 | others 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | .env 90 | .venv 91 | env/ 92 | venv/ 93 | ENV/ 94 | env.bak/ 95 | venv.bak/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | 110 | ### Python Patch ### 111 | .venv/ 112 | 113 | ### Python.VirtualEnv Stack ### 114 | # Virtualenv 115 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 116 | [Bb]in 117 | [Ii]nclude 118 | [Ll]ib 119 | [Ll]ib64 120 | [Ll]ocal 121 | [Ss]cripts 122 | pyvenv.cfg 123 | pip-selfcheck.json 124 | 125 | ### VisualStudioCode ### 126 | .vscode/* 127 | !.vscode/settings.json 128 | !.vscode/tasks.json 129 | !.vscode/launch.json 130 | !.vscode/extensions.json 131 | 132 | 133 | 134 | # End of https://www.gitignore.io/api/python,visualstudiocode -------------------------------------------------------------------------------- /1-Simple Client and Server/client.py: -------------------------------------------------------------------------------- 1 | '''Simple client program''' 2 | import socket 3 | import sys 4 | 5 | HOST = 'localhost' 6 | PORT = 4300 7 | 8 | 9 | def main(name: str): 10 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 11 | s.connect((HOST, PORT)) 12 | print('Connected to {}:{}'.format(HOST, PORT)) 13 | s.sendall("Hi, I'm {}".format(name).encode()) 14 | data = s.recv(1024) 15 | print('Received: {}'.format(data.decode())) 16 | s.close() 17 | print('Connection closed') 18 | 19 | 20 | if __name__ == '__main__': 21 | main(sys.argv[1]) 22 | -------------------------------------------------------------------------------- /1-Simple Client and Server/server.py: -------------------------------------------------------------------------------- 1 | '''Simple server program''' 2 | import socket 3 | 4 | HOST = '127.0.0.1' 5 | PORT = 4300 6 | 7 | 8 | def main(): 9 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 10 | s.bind((HOST, PORT)) 11 | s.listen(1) 12 | print('Listening on port {}'.format(PORT)) 13 | conn, addr = s.accept() 14 | with conn: 15 | print('Accepted connection from {}'.format(addr)) 16 | while True: 17 | data = conn.recv(1024) 18 | if not data: 19 | print('Connection closed') 20 | break 21 | name = data.decode().split()[2] 22 | conn.sendall("Hello, {}".format(name).encode()) 23 | 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /2-A Bit Complicated Client and Server - Data Retrieval/geo_client_tcp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | GEO TCP Client 3 | ''' 4 | #!/usr/bin/env python3 5 | 6 | from socket import socket, AF_INET, SOCK_STREAM 7 | 8 | HOST = 'localhost' 9 | PORT = 4300 10 | 11 | 12 | def client(): 13 | '''Main client loop''' 14 | with socket(AF_INET, SOCK_STREAM) as s: 15 | s.connect((HOST, PORT)) 16 | print('>You are connected to {}:{}'.format(HOST, PORT)) 17 | country = input('>Enter a country or BYE to quit\n') 18 | while country.upper() != "BYE": 19 | s.sendall(country.encode()) 20 | data = s.recv(1024) 21 | if data.decode() == "NOT FOUND": 22 | print("-There is no such country") 23 | else: 24 | print('+{}'.format(data.decode())) 25 | country = input('>Enter another country to try again or BYE to quit\n') 26 | s.close() 27 | print('Connection ') 28 | 29 | def main(): 30 | '''Main function''' 31 | client() 32 | 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /2-A Bit Complicated Client and Server - Data Retrieval/geo_server_tcp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | GEO TCP Server 3 | ''' 4 | #!/usr/bin/env python3 5 | 6 | from socket import socket, AF_INET, SOCK_STREAM 7 | import time 8 | 9 | FILE_NAME = 'geo_world.txt' 10 | HOST = 'localhost' 11 | PORT = 4300 12 | 13 | 14 | def read_file(filename: str) -> dict: 15 | '''Read world territories and their capitals from the provided file''' 16 | world = dict() 17 | 18 | print(time.strftime('%H:%M:%S Reading a file')) 19 | 20 | t = time.process_time() 21 | with open(filename, 'r') as f: 22 | for line in f: 23 | line = line.strip() 24 | line = line.split('-') 25 | world[line[0].strip()] = line[1].strip() 26 | elapsed_time = time.process_time() - t 27 | 28 | print(time.strftime('%H:%M:%S'), "Read in {0:.5f}".format(elapsed_time), "sec") 29 | 30 | return world 31 | 32 | 33 | def server(world: dict) -> None: 34 | '''Main server loop''' 35 | with socket(AF_INET, SOCK_STREAM) as s: 36 | s.bind((HOST, PORT)) 37 | s.listen(1) 38 | print(time.strftime('%H:%M:%S'), 'Listening on {}:{}'.format(HOST, PORT)) 39 | conn, addr = s.accept() 40 | with conn: 41 | print(time.strftime('%H:%M:%S'), 'Connected: {}'.format(addr[0])) 42 | while True: 43 | data = conn.recv(1024) 44 | if not data: 45 | print(time.strftime('%H:%M:%S'), 'Disconnected: {}'.format(addr[0])) 46 | break 47 | country = data.decode() 48 | print(time.strftime('%H:%M:%S'), 'User Query: {}'.format(country)) 49 | if country in world: 50 | conn.sendall(world[country].encode('utf-8')) 51 | else: 52 | conn.sendall("NOT FOUND".encode('utf-8')) 53 | 54 | 55 | def main(): 56 | '''Main function''' 57 | world = read_file(FILE_NAME) 58 | server(world) 59 | 60 | 61 | if __name__ == "__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /2-A Bit Complicated Client and Server - Data Retrieval/geo_world.txt: -------------------------------------------------------------------------------- 1 | Abkhazia - Sukhumi 2 | Afghanistan - Kabul 3 | Akrotiri and Dhekelia - Episkopi Cantonment 4 | Albania - Tirana 5 | Algeria - Algiers 6 | American Samoa - Pago Pago 7 | Andorra - Andorra la Vella 8 | Angola - Luanda 9 | Anguilla - The Valley 10 | Antigua and Barbuda - St. John's 11 | Argentina - Buenos Aires 12 | Armenia - Yerevan 13 | Aruba - Oranjestad 14 | Ascension Island - Georgetown 15 | Australia - Canberra 16 | Austria - Vienna 17 | Azerbaijan - Baku 18 | Bahamas - Nassau 19 | Bahrain - Manama 20 | Bangladesh - Dhaka 21 | Barbados - Bridgetown 22 | Belarus - Minsk 23 | Belgium - Brussels 24 | Belize - Belmopan 25 | Benin - Porto-Novo (official); Cotonou (de facto) 26 | Bermuda - Hamilton 27 | Bhutan - Thimphu 28 | Bolivia - Sucre (constitutional); La Paz (administrative) 29 | Bosnia and Herzegovina - Sarajevo 30 | Botswana - Gaborone 31 | Brazil - Brasília 32 | British Virgin Islands - Road Town 33 | Brunei - Bandar Seri Begawan 34 | Bulgaria - Sofia 35 | Burkina Faso - Ouagadougou 36 | Burundi - Bujumbura 37 | Cabo Verde - Praia 38 | Cambodia - Phnom Penh 39 | Cameroon - Yaoundé 40 | Canada - Ottawa 41 | Cayman Islands - George Town 42 | Central African Republic - Bangui 43 | Chad - N'Djamena 44 | Chechnya - Grozny 45 | Chile - Santiago 46 | China - Beijing 47 | Christmas Island - Flying Fish Cove 48 | Cocos (Keeling) Islands - West Island 49 | Colombia - Bogotá 50 | Comoros - Moroni 51 | Cook Islands - Avarua 52 | Costa Rica - San José 53 | Croatia - Zagreb 54 | Cuba - Havana 55 | Curaçao - Willemstad 56 | Cyprus - Nicosia 57 | Czech Republic - Prague 58 | Côte d'Ivoire - Yamoussoukro (official); Abidjan (former capital, still has many government offices) 59 | Democratic Republic of the Congo - Kinshasa 60 | Denmark - Copenhagen 61 | Djibouti - Djibouti 62 | Dominica - Roseau 63 | Dominican Republic - Santo Domingo 64 | East Timor (Timor-Leste) - Dili 65 | Easter Island - Hanga Roa 66 | Ecuador - Quito 67 | Egypt - Cairo 68 | El Salvador - San Salvador 69 | Equatorial Guinea - Malabo 70 | Eritrea - Asmara 71 | Estonia - Tallinn 72 | Ethiopia - Addis Ababa 73 | Falkland Islands - Stanley 74 | Faroe Islands - Tórshavn 75 | Federated States of Micronesia - Palikir 76 | Fiji - Suva 77 | Finland - Helsinki 78 | France - Paris 79 | French Guiana - Cayenne 80 | French Polynesia - Papeete 81 | Gabon - Libreville 82 | Gambia - Banjul 83 | Georgia - Tbilisi (official); Kutaisi (legislative) 84 | Germany - Berlin 85 | Ghana - Accra 86 | Gibraltar - Gibraltar 87 | Greece - Athens 88 | Greenland - Nuuk 89 | Grenada - St. George's 90 | Guadeloupe - Basse-Terre 91 | Guam - Hagåtña 92 | Guatemala - Guatemala City 93 | Guernsey - St. Peter Port 94 | Guinea - Conakry 95 | Guinea-Bissau - Bissau 96 | Guyana - Georgetown 97 | Haiti - Port-au-Prince 98 | Honduras - Tegucigalpa 99 | Hong Kong - Hong Kong 100 | Hungary - Budapest 101 | Iceland - Reykjavík 102 | India - New Delhi 103 | Indonesia - Jakarta 104 | Iran - Tehran 105 | Iraq - Baghdad 106 | Ireland - Dublin 107 | Isle of Man - Douglas 108 | Israel - Jerusalem (declared, de facto) 109 | Italy - Rome 110 | Jamaica - Kingston 111 | Japan - Tokyo 112 | Jersey - St. Helier 113 | Jordan - Amman 114 | Kazakhstan - Astana 115 | Kenya - Nairobi 116 | Kiribati - Tarawa 117 | Kosovo - Pristina 118 | Kuwait - Kuwait City 119 | Kyrgyzstan - Bishkek 120 | Laos - Vientiane 121 | Latvia - Riga 122 | Lebanon - Beirut 123 | Lesotho - Maseru 124 | Liberia - Monrovia 125 | Libya - Tripoli 126 | Liechtenstein - Vaduz 127 | Lithuania - Vilnius 128 | Luxembourg - Luxembourg 129 | Madagascar - Antananarivo 130 | Malawi - Lilongwe 131 | Malaysia - Kuala Lumpur (official, legislative and royal); Putrajaya (administrative and judicial) 132 | Maldives - Malé 133 | Mali - Bamako 134 | Malta - Valletta 135 | Marshall Islands - Majuro 136 | Martinique - Fort-de-France 137 | Mauritania - Nouakchott 138 | Mauritius - Port Louis 139 | Mayotte - Mamoudzou 140 | Mexico - Mexico City 141 | Moldova - Chișinău 142 | Monaco - Monaco 143 | Mongolia - Ulaanbaatar 144 | Montenegro - Podgorica (official); Cetinje (Old Royal Capital, present seat of the President) 145 | Montserrat - Plymouth (official); Brades Estate (de facto) 146 | Morocco - Rabat 147 | Mozambique - Maputo 148 | Myanmar - Naypyidaw 149 | Nagorno-Karabakh Republic - Stepanakert 150 | Namibia - Windhoek 151 | Nauru - Yaren (de facto) 152 | Nepal - Kathmandu 153 | Netherlands - Amsterdam 154 | New Caledonia - Nouméa 155 | New Zealand - Wellington 156 | Nicaragua - Managua 157 | Niger - Niamey 158 | Nigeria - Abuja 159 | Niue - Alofi 160 | Norfolk Island - Kingston 161 | North Korea - Pyongyang 162 | Northern Cyprus - Nicosia 163 | Northern Mariana Islands - Saipan 164 | Norway - Oslo 165 | Oman - Muscat 166 | Pakistan - Islamabad 167 | Palau - Ngerulmud 168 | Panama - Panama City 169 | Papua New Guinea - Port Moresby 170 | Paraguay - Asunción 171 | Peru - Lima 172 | Philippines - Manila 173 | Pitcairn - Adamstown 174 | Poland - Warsaw 175 | Portugal - Lisbon 176 | Puerto Rico - San Juan 177 | Qatar - Doha 178 | Republic of Macedonia - Skopje 179 | Republic of the Congo - Brazzaville 180 | Romania - Bucharest 181 | Russia - Moscow 182 | Rwanda - Kigali 183 | Réunion - Saint-Denis 184 | Sahrawi Arab Democratic Republic - Laayoune (declared); Tifariti (de facto) 185 | Saint Barthélemy - Gustavia 186 | Saint Helena - Jamestown 187 | Saint Kitts and Nevis - Basseterre 188 | Saint Lucia - Castries 189 | Saint Martin - Marigot 190 | Saint Pierre and Miquelon - St. Pierre 191 | Saint Vincent and the Grenadines - Kingstown 192 | Samoa - Apia 193 | San Marino - San Marino 194 | Saudi Arabia - Riyadh 195 | Senegal - Dakar 196 | Serbia - Belgrade 197 | Seychelles - Victoria 198 | Sierra Leone - Freetown 199 | Singapore - Singapore 200 | Sint Maarten - Philipsburg 201 | Slovakia - Bratislava 202 | Slovenia - Ljubljana 203 | Solomon Islands - Honiara 204 | Somalia - Mogadishu 205 | Somaliland - Hargeisa 206 | South Africa - Pretoria (executive); Bloemfontein (judicial); Cape Town (legislative) 207 | South Georgia and the South Sandwich Islands - King Edward Point 208 | South Korea - Seoul 209 | South Ossetia - Tskhinvali 210 | South Sudan - Juba 211 | Spain - Madrid 212 | Sri Lanka - Sri Jayawardenepura Kotte (official); Colombo (former capital, has some government offices) 213 | State of Palestine - East Jerusalem (declared); Ramallah (de facto) 214 | Sudan - Khartoum 215 | Suriname - Paramaribo 216 | Swaziland - Mbabane (administrative); Lobamba (royal and legislative) 217 | Sweden - Stockholm 218 | Switzerland - Bern 219 | Syria - Damascus 220 | São Tomé and Príncipe - São Tomé 221 | Taiwan - Taipei 222 | Tajikistan - Dushanbe 223 | Tanzania - Dodoma (official, legislative) 224 | Thailand - Bangkok 225 | Togo - Lomé 226 | Tonga - Nukuʻalofa 227 | Transnistria - Tiraspol 228 | Trinidad and Tobago - Port of Spain 229 | Tristan da Cunha - Edinburgh of the Seven Seas 230 | Tunisia - Tunis 231 | Turkey - Ankara 232 | Turkmenistan - Ashgabat 233 | Turks and Caicos Islands - Cockburn Town 234 | Tuvalu - Funafuti 235 | Uganda - Kampala 236 | Ukraine - Kyiv 237 | United Arab Emirates - Abu Dhabi 238 | United Kingdom - London 239 | United States - Washington, D.C. 240 | United States Virgin Islands - Charlotte Amalie 241 | Uruguay - Montevideo 242 | Uzbekistan - Tashkent 243 | Vanuatu - Port Vila 244 | Vatican City - Vatican City 245 | Venezuela - Caracas 246 | Vietnam - Hanoi 247 | Wallis and Futuna - Mata-Utu 248 | Yemen - Sana'a 249 | Zambia - Lusaka 250 | Zimbabwe - Harare 251 | -------------------------------------------------------------------------------- /3-DNS Resolver/README.md: -------------------------------------------------------------------------------- 1 | # Custom dig 2 | 3 | Complete the following programming project and push code to your GitHub repository. 4 | 5 | **Process records of type A (IPv4) or AAAA (IPv6) only. If a server returns CNAME record instead, ignore it.** 6 | 7 | Use *yahoo.com* as an example of a clean and simple response. 8 | 9 | Read record type (**A** or **AAAA**), domain name, and an optional DNS server as parameters passed to your program. 10 | 11 | ``` 12 | python3 resolver.py A luther.edu 1.1.1.1 13 | ``` 14 | or 15 | ``` 16 | python3 resolver.py AAAA yahoo.com 17 | ``` 18 | 19 | 1. Format a DNS request using the following values: 20 | 21 | * Transaction ID: auto-incremental or random 22 | * Flags: standard query (recursion bit set to 1, other bits are 0) 23 | * Questions: 1 24 | * Answer RRs: 0 25 | * Authority RRs: 0 26 | * Additional RRs: 0 27 | * Name: user-provided 28 | * Type: user-provided 29 | * Class: IN (0x0001) 30 | 31 | 2. If the DNS address is not specified, pick one of the well-known public servers. 32 | 33 | 3. Create a UDP socket connection and send the request to the DNS server. 34 | 35 | 4. Parse the response message from the DNS server to extract all addresses (there could be more than 1). 36 | 37 | 5. Print the address of the DNS server used. 38 | 39 | 6. Display the received information in the following format: 40 | 41 | ``` 42 | Name: 43 | TTL: record time-to-live 44 | Addresses: 45 | ``` 46 | 47 | 7. Pass all tests provided. 48 | 49 | ``` 50 | python3 -m pytest test_resolver.py 51 | ``` 52 | 53 | ## Approach 54 | 55 | * Look at a valid DNS request (eg. ping www.luther.edu and capture the traffic) 56 | 57 | ![DNS request](dns_query.png) 58 | 59 | * Analyze the structure of a message (see the links below for details) and replicate it 60 | 61 | ![DNS request](dns_query_hex.png) 62 | 63 | * Format your own message, byte by byte (you may want to use Python's bytearray for that) 64 | 65 | * Make your client format a message based on user input (domain, record type) 66 | 67 | * Send the message and receive the response 68 | 69 | * Parse the response and present the result (IP address). Consider simple cases (domain - one or more address(es)), ignore complex paypal-like resolutions with multiple pseudos. 70 | 71 | ## Functions 72 | 73 | > 'Begin at the beginning,' the King said gravely, 'and go on till you come to the end: then stop.' 74 | 75 | ### `val_to_2_bytes(value: int) -> list` 76 | 77 | `val_to_2_bytes` takes an integer and returns that number converted to a list of 2 bytes (numbers between 0 and 255). Use shift (>>) and binary and (&) to extract left and right 8 bits. This function is used extensively as many fields in the DNS request and response are using 2 bytes, even if a value fits 1 byte. 78 | 79 | ``` 80 | 43043 = 0b1010100000100011 => [0b10101000, 0b00100011] = [168, 35] 81 | left 8 right 8 82 | ``` 83 | 84 | ### `val_to_n_bytes(value: int, n_bytes: int) -> list` 85 | 86 | `val_to_n_bytes` takes an integer and the target list size, converting the number to a list of the specified size. Use shifting (<<, >>) and bit masking (&) in a loop to generate the list. You don't have to use this function but it's a great way to learn bitwise operations. 87 | 88 | ``` 89 | 430430 = 0b1101001000101011110 => [0b00000110, 0b10010001, 0b01011110] = [6, 145, 94] 90 | left 8 middle 8 right 8 91 | ``` 92 | 93 | ### `bytes_to_val(bytes_lst: list) -> int` 94 | 95 | `bytes_to_val` takes a list of bytes and returns their value as a single integer. Use shift (<<) and addition in a loop to construct the result. This function is used extensively as many DNS fields are stored in 2 bytes. 96 | 97 | ``` 98 | [6, 145, 94] = [0b110, 0b10010001, 0b01011110] => 0b1101001000101011110 = 430430 99 | ``` 100 | 101 | ### `get_2_bits(bytes_lst: list) -> int` 102 | 103 | `get_2_bits` extracts the leftmost 2 bits from a 2-byte sequence. Use a simple shift to extract the target bits. This function is used to determine whether the domain is stored in the answer as a label or a pointer. See the provided references for details on those two formats. 104 | 105 | ``` 106 | 0xc00c = 0b1100000000001100 => leftmost 2 bits are 0b11 = 3 107 | ``` 108 | 109 | ### `get_offset(bytes_lst: list) -> int` 110 | 111 | `get_offset` extracts the rightmost 14 bits from a 2-byte sequence. This function can be used to extract the location of the domain name inside a response. Note that a response may contain either labels or pointers, so don't rely on the *magic* of `0xc00c`. A more descriptive name for this function is *get_domain_name_location_within_a_server_response*. Do not confuse the offset found by this function with the offset of answers within the response. 112 | 113 | ``` 114 | 0xc00c = 0b1100000000001100 => rightmost 14 bits are 0b1100= 12 115 | ``` 116 | 117 | ### `parse_cli_query(filename, q_type, q_domain, q_server=None) -> tuple` 118 | 119 | `parse_cli_query` takes a filename, a query type, the domain name to resolve, and an optional server address as parameters and returns a tuple of the numeric value of the query type (as found in the `DNS_TYPES` dictionary), domain name (as a list of strings), and the server address. If the server address is not specified, pick one randomly as follows: `choice(PUBLIC_DNS_SERVER)`. 120 | 121 | ### `format_query(q_type: int, q_domain: list) -> bytearray` 122 | 123 | `format_query` takes the query type and the domain name as parameters and builds a query as a bytearray. Bytearrays are mutable byte sequences in Python, so you should start with an empty one and use `append` or `extend` to form a valid DNS query. Transaction id should be chosen at random as follows: `randint(0, 65535)`. Use default value, `0x100` for the flags. The domain name should be in the **QNAME** format, terminated by `\0`. 124 | 125 | ``` 126 | 56 f0 01 00 00 01 00 00 00 00 00 00 06 6c 75 74 68 65 72 03 65 64 75 00 00 01 00 01 127 | |---| |---| |---| |---| |---| |---| |------------------| |---------| || |---| |---| 128 | |id | |flags, # of questions etc | | luther | | edu | \0 |typ| |cls| 129 | ``` 130 | 131 | ### `send_request(q_message: bytearray, q_server: str) -> bytes` 132 | 133 | `send_request` takes the formatted message and the server address and sends the DNS request. This function returns the DNS response for the parser to process. Implemented for your convenience. 134 | 135 | ### `parse_response(resp_bytes: bytes)` 136 | 137 | `parse_response` takes bytes received from the server and returns a list (*or a tuple*, it doesn't matter) where each item is a tuple of the domain name, TTL, and the address, as extracted from the server response. This function processes the response header (first 12 bytes and the query), calls `parse_answers` to parse the specific answer(s), and returns the results returned by `parse_answers`. You don't need to validate values in the response (i.e. transaction id and flags) but you have to extract the number of answers from the header and the starting byte of the answers. 138 | 139 | ### `parse_answers(resp_bytes: bytes, offset: int, rr_ans: int) -> list` 140 | 141 | `parse_answers` takes the response message bytes, starting position for the answer(s) within the response, and the number of answers. It returns a list of tuples (domain, ttl, address). Do not confuse the *offset* in this function (a better(?) name would be *number_of_bytes_from_the_beginning_of_the_response_to_the_first_answer*) and the *domain_name_start_offset*. Keep in mind that the domain name may be in different format, *label* or *pointer*. You should be able to process both. 142 | 143 | Once you've processed an answer, add the results to the list and move to the next one, if present. Once all the answers are collected, return the list of tuples. 144 | 145 | ``` 146 | c0 0c 00 01 00 01 00 00 00 05 00 04 ae 81 19 aa 147 | |---| |---| |---| |---------| |---| |---------| 148 | |ptr| |typ| |cls| | ttl | |len| | address | 149 | ``` 150 | 151 | ### `parse_address_a(addr_len: int, addr_bytes: bytes) -> str` 152 | 153 | `parse_address_a` extracts IPv4 address from the response and returns it in the dotted-decimal notation. 154 | 155 | ### `parse_address_aaaa(addr_len: int, addr_bytes: bytes) -> str` 156 | 157 | `parse_address_aaaa` extracts IPv6 address from the response and returns it in the hex-colon notation. 158 | 159 | ### `resolve(query: str) -> None` 160 | 161 | `resolve` calls and other functions and prints the results. It is implemented for your convenience. 162 | 163 | ## Resources 164 | 165 | * [RFC 1035 - Domain names - implementation and specification](https://tools.ietf.org/html/rfc1035) 166 | 167 | * [The TCP/IP Guide - DNS Messaging and Message, Resource Record and Master File Formats](http://www.tcpipguide.com/free/t_DNSMessagingandMessageResourceRecordandMasterFileF.htm) 168 | 169 | * [Chapter 15 DNS Messages](http://www.zytrax.com/books/dns/ch15/) 170 | 171 | * [Domain Name System (DNS) Parameters](http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml) 172 | 173 | * [Python Bytes, Bytearray - w3resource](https://www.w3resource.com/python/python-bytes.php) 174 | -------------------------------------------------------------------------------- /3-DNS Resolver/clarifications/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/clarifications/1.jpg -------------------------------------------------------------------------------- /3-DNS Resolver/clarifications/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/clarifications/2.jpg -------------------------------------------------------------------------------- /3-DNS Resolver/clarifications/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/clarifications/3.jpg -------------------------------------------------------------------------------- /3-DNS Resolver/clarifications/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/clarifications/4.jpg -------------------------------------------------------------------------------- /3-DNS Resolver/clarifications/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/clarifications/5.jpg -------------------------------------------------------------------------------- /3-DNS Resolver/dns_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/dns_query.png -------------------------------------------------------------------------------- /3-DNS Resolver/dns_query_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/3-DNS Resolver/dns_query_hex.png -------------------------------------------------------------------------------- /3-DNS Resolver/test_resolver.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Testing the DNS resolver 3 | ''' 4 | #!/usr/bin/python3 5 | 6 | 7 | from random import seed 8 | import pytest 9 | from resolver import val_to_2_bytes 10 | from resolver import val_to_n_bytes 11 | from resolver import bytes_to_val 12 | from resolver import get_2_bits 13 | from resolver import get_domain_name_location 14 | from resolver import parse_cli_query 15 | from resolver import format_query 16 | from resolver import parse_response 17 | from resolver import parse_answers 18 | from resolver import parse_address_a 19 | from resolver import parse_address_aaaa 20 | from resolver import PUBLIC_DNS_SERVER 21 | 22 | seed(430) 23 | 24 | 25 | class TestResolver: 26 | '''Testing DNS resolver''' 27 | 28 | @pytest.fixture(scope='function', autouse=True) 29 | def setup_class(self): 30 | '''Setting up''' 31 | pass 32 | 33 | def test_val_to_bytes(self): 34 | '''Convert a value to 2 bytes''' 35 | assert val_to_2_bytes(43043) == [168, 35] 36 | assert val_to_n_bytes(430430, 3) == [6, 145, 94] 37 | 38 | def test_bytes_to_val(self): 39 | '''Convert list of bytes to a value''' 40 | assert bytes_to_val([6, 145, 94]) == 430430 41 | 42 | def test_get_2_bits(self): 43 | '''Get 2 bits''' 44 | assert get_2_bits([200, 100]) == 3 45 | 46 | def test_get_domain_name_location(self): 47 | '''Get domain name location''' 48 | assert get_domain_name_location([200, 100]) == 2148 49 | 50 | def test_parse_cli_query(self): 51 | '''Parse command-line arguments''' 52 | # assert parse_cli_query('resolver.py', 'A', 'luther.edu') == (1, ['luther', 'edu'], '8.26.56.26') 53 | cli_query = parse_cli_query('resolver.py', 'A', 'luther.edu') 54 | assert cli_query[0] == 1 55 | assert cli_query[1] == ['luther', 'edu'] 56 | assert cli_query[2] in PUBLIC_DNS_SERVER 57 | assert parse_cli_query('resolver.py', 'A', 'luther.edu', '1.0.0.1') == (1, ['luther', 'edu'], '1.0.0.1') 58 | # assert parse_cli_query('resolver.py', 'AAAA', 'luther.edu') == (28, ['luther', 'edu'], '8.8.4.4') 59 | cli_query = parse_cli_query('resolver.py', 'AAAA', 'luther.edu') 60 | assert cli_query[0] == 28 61 | assert cli_query[1] == ['luther', 'edu'] 62 | assert cli_query[2] in PUBLIC_DNS_SERVER 63 | with pytest.raises(ValueError) as excinfo: 64 | parse_cli_query('resolver.py', 'MX', 'luther.edu') 65 | exception_msg = excinfo.value.args[0] 66 | assert exception_msg == 'Unknown query type' 67 | 68 | def test_format_query(self): 69 | '''Format a query''' 70 | assert format_query(1, ['luther', 'edu']) == b'OB\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06luther\x03edu\x00\x00\x01\x00\x01' 71 | 72 | def test_parse_response(self): 73 | '''Parse the response''' 74 | assert parse_response(b'\xc7D\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x06luther\x03edu\x00\x00\x01' + 75 | b'\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x01,\x00\x04\xae\x81\x19\xaa') == \ 76 | [('luther.edu', 300, '174.129.25.170')] 77 | 78 | assert parse_response(b'r\xd4\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x05yahoo\x03com' + 79 | b'\x00\x00\x01\x00\x01\x05yahoo\x03com\x00\x00\x01\x00\x01\x00\x00' + 80 | b'\x00\x05\x00\x04b\x89\xf6\x07\x05yahoo\x03com\x00\x00\x01\x00\x01' + 81 | b'\x00\x00\x00\x05\x00\x04H\x1e#\t\x05yahoo\x03com\x00\x00\x01\x00' + 82 | b'\x01\x00\x00\x00\x05\x00\x04H\x1e#\n\x05yahoo\x03com\x00\x00\x01' + 83 | b'\x00\x01\x00\x00\x00\x05\x00\x04b\x8a\xdb\xe7\x05yahoo\x03com\x00' + 84 | b'\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04b\x89\xf6\x08\x05yahoo\x03com' + 85 | b'\x00\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04b\x8a\xdb\xe8') == \ 86 | [ 87 | ('yahoo.com', 5, '98.137.246.7'), 88 | ('yahoo.com', 5, '72.30.35.9'), 89 | ('yahoo.com', 5, '72.30.35.10'), 90 | ('yahoo.com', 5, '98.138.219.231'), 91 | ('yahoo.com', 5, '98.137.246.8'), 92 | ('yahoo.com', 5, '98.138.219.232') 93 | ] 94 | assert parse_response(b'k\xfb\x81\x80\x00\x01\x00\x06\x00\x00\x00\x00\x05yahoo\x03com' + 95 | b'\x00\x00\x1c\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x04T\x00\x10 ' + 96 | b'\x01I\x98\x00X\x186\x00\x00\x00\x00\x00\x00\x00\x11\xc0\x0c\x00\x1c' + 97 | b'\x00\x01\x00\x00\x04T\x00\x10 \x01I\x98\x00X\x186\x00\x00\x00\x00\x00' + 98 | b'\x00\x00\x10\xc0\x0c\x00\x1c\x00\x01\x00\x00\x04T\x00\x10 \x01I\x98' + 99 | b'\x00D\x04\x1d\x00\x00\x00\x00\x00\x00\x00\x04\xc0\x0c\x00\x1c\x00' + 100 | b'\x01\x00\x00\x04T\x00\x10 \x01I\x98\x00D\x04\x1d\x00\x00\x00\x00' + 101 | b'\x00\x00\x00\x03\xc0\x0c\x00\x1c\x00\x01\x00\x00\x04T\x00\x10 \x01I' + 102 | b'\x98\x00\x0c\x10#\x00\x00\x00\x00\x00\x00\x00\x05\xc0\x0c\x00\x1c' + 103 | b'\x00\x01\x00\x00\x04T\x00\x10 \x01I\x98\x00\x0c\x10#\x00\x00\x00\x00\x00\x00\x00\x04') == \ 104 | [ 105 | ('yahoo.com', 1108, '2001:4998:58:1836:0:0:0:11'), 106 | ('yahoo.com', 1108, '2001:4998:58:1836:0:0:0:10'), 107 | ('yahoo.com', 1108, '2001:4998:44:41d:0:0:0:4'), 108 | ('yahoo.com', 1108, '2001:4998:44:41d:0:0:0:3'), 109 | ('yahoo.com', 1108, '2001:4998:c:1023:0:0:0:5'), 110 | ('yahoo.com', 1108, '2001:4998:c:1023:0:0:0:4') 111 | ] 112 | 113 | def test_parse_answers(self): 114 | '''Parse answers''' 115 | assert parse_answers(b'tH\x81\x80\x00\x01\x00\x01\x00\x03\x00\x01\x06luther\x03edu\x00\x00\x01\x00\x01' + 116 | b'\xc0\x0c\x00\x01\x00\x01\x00\x00\x01,\x00\x04\xae\x81\x19\xaa\xc0\x0c\x00\x02\x00' + 117 | b'\x01\x00\x01Q\x80\x00\x10\x05dns-2\x07iastate\xc0\x13\xc0\x0c\x00\x02\x00\x01\x00' + 118 | b'\x01Q\x80\x00\n\x03dns\x03uni\xc0\x13\xc0\x0c\x00\x02\x00\x01\x00\x01Q\x80\x00\t' + 119 | b'\x06martin\xc0\x0c\xc0j\x00\x01\x00\x01\x00\x01Q\x80\x00\x04\xc0\xcb\xc4\x14', 28, 1) == \ 120 | [('luther.edu', 300, '174.129.25.170')] 121 | 122 | assert parse_answers(b'{\xae\x81\x80\x00\x01\x00\x06\x00\x05\x00\x08\x05yahoo\x03com' + 123 | b'\x00\x00\x1c\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x04\xe9\x00' + 124 | b'\x10 \x01I\x98\x00D\x04\x1d\x00\x00\x00\x00\x00\x00\x00\x04\xc0\x0c' + 125 | b'\x00\x1c\x00\x01\x00\x00\x04\xe9\x00\x10 \x01I\x98\x00D\x04\x1d\x00' + 126 | b'\x00\x00\x00\x00\x00\x00\x03\xc0\x0c\x00\x1c\x00\x01\x00\x00\x04\xe9' + 127 | b'\x00\x10 \x01I\x98\x00X\x186\x00\x00\x00\x00\x00\x00\x00\x11\xc0\x0c' + 128 | b'\x00\x1c\x00\x01\x00\x00\x04\xe9\x00\x10 \x01I\x98\x00\x0c\x10#\x00' + 129 | b'\x00\x00\x00\x00\x00\x00\x04\xc0\x0c\x00\x1c\x00\x01\x00\x00\x04\xe9' + 130 | b'\x00\x10 \x01I\x98\x00\x0c\x10#\x00\x00\x00\x00\x00\x00\x00\x05\xc0' + 131 | b'\x0c\x00\x1c\x00\x01\x00\x00\x04\xe9\x00\x10 \x01I\x98\x00X\x186\x00' + 132 | b'\x00\x00\x00\x00\x00\x00\x10\xc0\x0c\x00\x02\x00\x01\x00\x00s0\x00' + 133 | b'\x06\x03ns1\xc0\x0c\xc0\x0c\x00\x02\x00\x01\x00\x00s0\x00\x06\x03ns3' + 134 | b'\xc0\x0c\xc0\x0c\x00\x02\x00\x01\x00\x00s0\x00\x06\x03ns5\xc0\x0c\xc0' + 135 | b'\x0c\x00\x02\x00\x01\x00\x00s0\x00\x06\x03ns2\xc0\x0c\xc0\x0c\x00\x02' + 136 | b'\x00\x01\x00\x00s0\x00\x06\x03ns4\xc0\x0c\xc0\xcf\x00\x1c\x00\x01\x00' + 137 | b'\x00CX\x00\x10 \x01I\x98\x010\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01' + 138 | b'\xc1\x05\x00\x1c\x00\x01\x00\x00\xda\xdb\x00\x10 \x01I\x98\x01@\x00' + 139 | b'\x00\x00\x00\x00\x00\x00\x00\x10\x02\xc0\xe1\x00\x1c\x00\x01\x00\x00' + 140 | b'\xd9\xb0\x00\x10$\x06\x86\x00\x00\xb8\xfe\x03\x00\x00\x00\x00\x00\x00' + 141 | b'\x10\x03\xc0\xcf\x00\x01\x00\x01\x00\x11f\xdc\x00\x04D\xb4\x83\x10\xc1' + 142 | b'\x05\x00\x01\x00\x01\x00\x11f\xde\x00\x04D\x8e\xff\x10\xc0\xe1\x00\x01' + 143 | b'\x00\x01\x00\x0f\xfe\xc3\x00\x04\xcbT\xdd5\xc1\x17\x00\x01\x00\x01\x00' + 144 | b'\x11\x8ah\x00\x04b\x8a\x0b\x9d\xc0\xf3\x00\x01\x00\x01\x00\x11PF\x00\x04w\xa0\xfdS', 27, 6) == \ 145 | [ 146 | ('yahoo.com', 1257, '2001:4998:44:41d:0:0:0:4'), 147 | ('yahoo.com', 1257, '2001:4998:44:41d:0:0:0:3'), 148 | ('yahoo.com', 1257, '2001:4998:58:1836:0:0:0:11'), 149 | ('yahoo.com', 1257, '2001:4998:c:1023:0:0:0:4'), 150 | ('yahoo.com', 1257, '2001:4998:c:1023:0:0:0:5'), 151 | ('yahoo.com', 1257, '2001:4998:58:1836:0:0:0:10') 152 | ] 153 | 154 | def test_parse_address_a(self): 155 | '''Parse IPv4 address''' 156 | assert parse_address_a(4, b'\xae\x81\x19\xaa') == '174.129.25.170' 157 | 158 | def test_parse_address_aaaa(self): 159 | '''Parse IPv6 address''' 160 | assert parse_address_aaaa(16, b' \x01I\x98\x00\x0c\x10#\x00\x00\x00\x00\x00\x00\x00\x04\xc0') == '2001:4998:c:1023:0:0:0:4' 161 | 162 | 163 | if __name__ == '__main__': 164 | pytest.main(['test_resolver.py']) 165 | -------------------------------------------------------------------------------- /4-DNS Server/README.md: -------------------------------------------------------------------------------- 1 | # Custom BIND 2 | 3 | Complete the following programming project and push code to your GitHub repository. 4 | 5 | **Process records of type A (IPv4) or AAAA (IPv6) only. If a client requests anything else, ignore it.** 6 | 7 | Use your DNS resolver to initiate requests to the server. 8 | 9 | ``` 10 | python3 nameserver.py zoo.zone 11 | ``` 12 | 13 | 1. Read the zone file *zoo.zone* and resolve names found there. 14 | 15 | 2. Create a UDP socket connection and wait for a message from the DNS resolver. 16 | 17 | 3. Parse the DNS request. 18 | 19 | 4. Find the domain in the zone file. 20 | 21 | 5. Format the response, byte by byte (you may want to use Python's bytearray for that). 22 | 23 | 6. Return answer(s). 24 | 25 | 7. Pass all tests provided. 26 | 27 | ``` 28 | python3 -m pytest test_nameserver.py 29 | ``` 30 | 31 | 8. Use your `resolver.py` or `nslookup` to resolve some somain name from the zone file. Note that you need to connect to port **43053** on your **localhost**. 32 | 33 | 34 | ## Usage example 35 | 36 | Using `resolver.py` to resolve type **A** address. 37 | 38 | ``` 39 | > python3 resolver.py A ant.cs430.luther.edu 127.0.0.1 40 | DNS server used: 127.0.0.1 41 | Domain: ant.cs430.luther.edu 42 | TTL: 3600 43 | Address: 185.84.224.89 44 | Domain: ant.cs430.luther.edu 45 | TTL: 3600 46 | Address: 199.83.67.158 47 | ``` 48 | 49 | Using `resolver.py` to resolve type **AAAA** address. 50 | 51 | ``` 52 | python3 resolver.py AAAA ant.cs430.luther.edu 127.0.0.1 53 | DNS server used: 127.0.0.1 54 | Domain: ant.cs430.luther.edu 55 | TTL: 3600 56 | Address: 4a9a:70ec:3ac0:c684:359e:8d37:9486:5959 57 | ``` 58 | 59 | Using `nslookup` to resolve type **A** address. 60 | 61 | ``` 62 | > nslookup -port=43053 -type=A ant.cs430.luther.edu 127.0.0.1 63 | Server: 127.0.0.1 64 | Address: 127.0.0.1#43053 65 | 66 | Non-authoritative answer: 67 | Name: ant.cs430.luther.edu 68 | Address: 185.84.224.89 69 | Name: ant.cs430.luther.edu 70 | Address: 199.83.67.158 71 | ``` 72 | 73 | Using `nslookup` to resolve type **AAAA** address. 74 | 75 | ``` 76 | > nslookup -port=43053 -type=AAAA ant.cs430.luther.edu 127.0.0.1 77 | Server: 127.0.0.1 78 | Address: 127.0.0.1#43053 79 | 80 | Non-authoritative answer: 81 | Name: ant.cs430.luther.edu 82 | Address: 4a9a:70ec:3ac0:c684:359e:8d37:9486:5959 83 | ``` 84 | 85 | ## Approach 86 | 87 | * Look at a valid DNS response (eg. ping www.luther.edu and capture the traffic) 88 | 89 | * Analyze the structure of a message (see the links below for details) and replicate it 90 | 91 | ## Functions 92 | 93 | ### val_to_bytes(value: int, n_bytes: int) -> list 94 | 95 | `val_to_bytes` takes an integer and a number of bytes and returns that integer as a list of the specified length. Most fields in DNS response use 2 bytes, but TTL uses 4 bytes. Use shift (<<, >>) and masking (&) to generate the list. 96 | 97 | ### bytes_to_val(bytes_lst: list) -> int 98 | 99 | `bytes_to_val` takes a list of bytes (values 0..255) and returns the value. Most values in DNS use 2 bytes, but you should implement a more generic algorithm to process a list of any length. 100 | 101 | ### get_left_bits(bytes_lst: list, n_bits: int) -> int 102 | 103 | `get_left_bits` takes a 2-byte list and a number *n* and returns leftmost *n* bits of that sequence as an integer. 104 | 105 | ### get_right_bits(bytes_lst: list, n_bits: int) -> int 106 | 107 | `get_right_bits` takes a 2-byte list and a number *n* and returns rightmost *n* bits of that sequence as an integer. 108 | 109 | ### read_zone_file(filename: str) -> tuple 110 | 111 | `read_zone_file` takes file name as a parameter and reads the **zone** from that file. This function builds a dictionary of the following format: `{domain: [(ttl, class, type, address)]}` where each record is a list of tuples (answers). The function should return a tuple of `(origin, zone_dict)`. If the requested domain is not in our zon, `parse_request` should raise a `ValueError`. Note that the records in the zone file may be incomplete (missing a domain name or TTL). The missing domain name should be replaced with the one from the previous line, missing TTL should be replaced with the default one (2nd line of the zone file). If a record contains multiple answers, return them all. 112 | 113 | ### parse_request(origin: str, msg_req: bytes) -> tuple 114 | 115 | `parse_request` takes `origin` and the request bytes and returns a tuple of (transaction id, domain, query type, query). The query is required as it is included in the response. This function must raise `ValueError`s if the type, class, or zone (origin) cannot be processed. Those exceptions are caught in the `run` function. 116 | 117 | ``` 118 | 56 f0 01 00 00 01 00 00 00 00 00 00 06 6c 75 74 68 65 72 03 65 64 75 00 00 01 00 01 119 | |---| |---| |---| |---| |---| |---| |------------------| |---------| || |---| |---| 120 | |id | |flags, # of questions etc | | luther | | edu | \0 |typ| |cls| 121 | |------------------query----------------------| 122 | ``` 123 | 124 | ### format_response(zone: dict, trans_id: int, qry_name: str, qry_type: int, qry: bytearray) -> bytearray 125 | 126 | `format_response` takes the zone dictionary, transaction_id, domain name, and the query. It formats the DNS response (bytearray) based on those values and returns it to the calling function. Your should either *label* or *pointer* to format the domain name. 127 | 128 | ### run(filename: str) -> None 129 | 130 | `run` is the main loop of the server and is implemented for your convenience. 131 | 132 | ## Resources 133 | 134 | * [RFC 1034 - Domain names - concepts and facilities](https://tools.ietf.org/html/rfc1034) 135 | 136 | * [RFC 1035 - Domain names - implementation and specification](https://tools.ietf.org/html/rfc1035) 137 | 138 | * [The TCP/IP Guide - DNS Messaging and Message, Resource Record and Master File Formats](http://www.tcpipguide.com/free/t_DNSMessagingandMessageResourceRecordandMasterFileF.htm) 139 | 140 | * [Chapter 15 DNS Messages](http://www.zytrax.com/books/dns/ch15/) 141 | 142 | * [Domain Name System (DNS) Parameters](http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml) 143 | -------------------------------------------------------------------------------- /4-DNS Server/cs430.zone: -------------------------------------------------------------------------------- 1 | $ORIGIN cs430.luther.edu. ; This is a comment 2 | $TTL 86400 ; 24 hours could have been written as 24h or 1d 3 | ; $TTL used for all RRs without explicit TTL value 4 | @ 1D IN SOA ns.cs430.luther.edu. admin.cs430.luther.edu. ( 5 | 2018091601 ; serial 6 | 3H ; refresh 7 | 15 ; retry 8 | 1w ; expire 9 | 3h ; nxdomain ttl 10 | ) 11 | IN NS ns.cs430.luther.edu. ; in the domain 12 | IN MX 10 mail.cs430.luther.edu. ; mail server 13 | IN MX 20 mail2.cs430.luther.edu. ; mail server 14 | ; server host definitions 15 | cs430.luther.edu. A 192.168.0.2 16 | AAAA 2001:db8:10::2 17 | ns IN A 192.168.0.1 ; name server definition 18 | www IN CNAME cs430.luther.edu. ; web server definition 19 | mail IN A 192.168.0.3 ; mail server definition 20 | www TXT "This is a web server" ; text description 21 | roman 1s IN A 1.2.3.4 22 | 1h IN A 1.2.3.5 23 | IN AAAA abcd:abcd:abcd:abcd:1234:1234:1234:1234 -------------------------------------------------------------------------------- /4-DNS Server/nameserver.md: -------------------------------------------------------------------------------- 1 | # Custom BIND 2 | 3 | Complete the following programming project and push code to your GitHub repository. 4 | 5 | **Process records of type A (IPv4) or AAAA (IPv6) only. If a client requests anything else, ignore it.** 6 | 7 | Use your DNS resolver or nslookup to initiate requests to the server. 8 | 9 | ``` 10 | python3 nameserver.py zoo.zone 11 | ``` 12 | 13 | 1. Read the zone file *zoo.zone* and resolve names found there. You **do not** need to process *cs430.zone*. 14 | 15 | 2. Create a UDP socket connection and wait for a message from the DNS resolver. 16 | 17 | 3. Parse the DNS request. 18 | 19 | 4. Find the domain in the zone file. 20 | 21 | 5. Format the response, byte by byte (you may want to use Python's bytearray for that). 22 | 23 | 6. Return answer(s). 24 | 25 | 7. Pass all tests provided. 26 | 27 | ``` 28 | python3 -m pytest test_nameserver.py 29 | ``` 30 | 31 | 8. Use your `resolver.py` or `nslookup` to resolve some somain name from the zone file. Note that you need to connect to port **43053** on your **localhost**. 32 | 33 | 34 | ## Usage example 35 | 36 | Using `resolver.py` to resolve type **A** address. 37 | 38 | ``` 39 | > python3 resolver.py A ant.cs430.luther.edu 127.0.0.1 40 | DNS server used: 127.0.0.1 41 | Domain: ant.cs430.luther.edu 42 | TTL: 3600 43 | Address: 185.84.224.89 44 | Domain: ant.cs430.luther.edu 45 | TTL: 3600 46 | Address: 199.83.67.158 47 | ``` 48 | 49 | Using `resolver.py` to resolve type **AAAA** address. 50 | 51 | ``` 52 | python3 resolver.py AAAA ant.cs430.luther.edu 127.0.0.1 53 | DNS server used: 127.0.0.1 54 | Domain: ant.cs430.luther.edu 55 | TTL: 3600 56 | Address: 4a9a:70ec:3ac0:c684:359e:8d37:9486:5959 57 | ``` 58 | 59 | Using `nslookup` to resolve type **A** address. 60 | 61 | ``` 62 | > nslookup -port=43053 -type=A ant.cs430.luther.edu 127.0.0.1 63 | Server: 127.0.0.1 64 | Address: 127.0.0.1#43053 65 | 66 | Non-authoritative answer: 67 | Name: ant.cs430.luther.edu 68 | Address: 185.84.224.89 69 | Name: ant.cs430.luther.edu 70 | Address: 199.83.67.158 71 | ``` 72 | 73 | Using `nslookup` to resolve type **AAAA** address. 74 | 75 | ``` 76 | > nslookup -port=43053 -type=AAAA ant.cs430.luther.edu 127.0.0.1 77 | Server: 127.0.0.1 78 | Address: 127.0.0.1#43053 79 | 80 | Non-authoritative answer: 81 | Name: ant.cs430.luther.edu 82 | Address: 4a9a:70ec:3ac0:c684:359e:8d37:9486:5959 83 | ``` 84 | 85 | ## Approach 86 | 87 | * Look at a valid DNS response (eg. ping www.luther.edu and capture the traffic) 88 | 89 | * Analyze the structure of a message (see the links below for details) and replicate it 90 | 91 | ## Functions 92 | 93 | ### val_to_bytes(value: int, n_bytes: int) -> list 94 | 95 | `val_to_bytes` takes an integer and a number of bytes and returns that integer as a list of the specified length. Most fields in DNS response use 2 bytes, but TTL uses 4 bytes. Use shift (<<, >>) and masking (&) to generate the list. 96 | 97 | ### bytes_to_val(bytes_lst: list) -> int 98 | 99 | `bytes_to_val` takes a list of bytes (values 0..255) and returns the value. Most values in DNS use 2 bytes, but you should implement a more generic algorithm to process a list of any length. 100 | 101 | ### get_left_bits(bytes_lst: list, n_bits: int) -> int 102 | 103 | `get_left_bits` takes a 2-byte list and a number *n* and returns leftmost *n* bits of that sequence as an integer. 104 | 105 | ### get_right_bits(bytes_lst: list, n_bits: int) -> int 106 | 107 | `get_right_bits` takes a 2-byte list and a number *n* and returns rightmost *n* bits of that sequence as an integer. 108 | 109 | ### read_zone_file(filename: str) -> tuple 110 | 111 | `read_zone_file` takes file name as a parameter and reads the **zone** from that file. This function builds a dictionary of the following format: `{domain: [(ttl, class, type, address)]}` where each record is a list of tuples (answers). The function should return a tuple of `(origin, zone_dict)`. If the requested domain is not in our zon, `parse_request` should raise a `ValueError`. Note that the records in the zone file may be incomplete (missing a domain name or TTL). The missing domain name should be replaced with the one from the previous line, missing TTL should be replaced with the default one (2nd line of the zone file). If a record contains multiple answers, return them all. 112 | 113 | ### parse_request(origin: str, msg_req: bytes) -> tuple 114 | 115 | `parse_request` takes `origin` and the request bytes and returns a tuple of (transaction id, domain, query type, query). The query is required as it is included in the response. This function must raise `ValueError`s if the type, class, or zone (origin) cannot be processed. Those exceptions are caught in the `run` function. 116 | 117 | ``` 118 | 56 f0 01 00 00 01 00 00 00 00 00 00 06 6c 75 74 68 65 72 03 65 64 75 00 00 01 00 01 119 | |---| |---| |---| |---| |---| |---| |------------------| |---------| || |---| |---| 120 | |id | |flags, # of questions etc | | luther | | edu | \0 |typ| |cls| 121 | |------------------query----------------------| 122 | ``` 123 | 124 | ### format_response(zone: dict, trans_id: int, qry_name: str, qry_type: int, qry: bytearray) -> bytearray 125 | 126 | `format_response` takes the zone dictionary, transaction_id, domain name, and the query. It formats the DNS response (bytearray) based on those values and returns it to the calling function. Your should either *label* or *pointer* to format the domain name. 127 | 128 | ### run(filename: str) -> None 129 | 130 | `run` is the main loop of the server and is implemented for your convenience. 131 | 132 | ## Resources 133 | 134 | * [RFC 1034 - Domain names - concepts and facilities](https://tools.ietf.org/html/rfc1034) 135 | 136 | * [RFC 1035 - Domain names - implementation and specification](https://tools.ietf.org/html/rfc1035) 137 | 138 | * [The TCP/IP Guide - DNS Messaging and Message, Resource Record and Master File Formats](http://www.tcpipguide.com/free/t_DNSMessagingandMessageResourceRecordandMasterFileF.htm) 139 | 140 | * [Chapter 15 DNS Messages](http://www.zytrax.com/books/dns/ch15/) 141 | 142 | * [Domain Name System (DNS) Parameters](http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml) 143 | -------------------------------------------------------------------------------- /4-DNS Server/nameserver.py: -------------------------------------------------------------------------------- 1 | ''' 2 | DNS Name Server 3 | ''' 4 | #!/usr/bin/env python3 5 | 6 | import sys 7 | from random import randint, choice 8 | from socket import socket, SOCK_DGRAM, AF_INET 9 | 10 | 11 | HOST = "localhost" 12 | PORT = 43053 13 | 14 | DNS_TYPES = { 15 | 1: 'A', 16 | 2: 'NS', 17 | 5: 'CNAME', 18 | 12: 'PTR', 19 | 15: 'MX', 20 | 16: 'TXT', 21 | 28: 'AAAA' 22 | } 23 | 24 | TTL_SEC = { 25 | '1s': 1, 26 | '1m': 60, 27 | '1h': 60*60, 28 | '1d': 60*60*24, 29 | '1w': 60*60*24*7, 30 | '1y': 60*60*24*365 31 | } 32 | 33 | 34 | def val_to_bytes(value: int, n_bytes: int) -> list: 35 | '''Split a value into n bytes''' 36 | 37 | ''' 38 | val_to_bytes takes an integer and a number of bytes and returns that integer as a list of the specified length. Most fields in DNS response use 2 bytes, but TTL uses 4 bytes. Use shift (<<, >>) and masking (&) to generate the list. 39 | ''' 40 | 41 | try: 42 | bytes = list(value.to_bytes(n_bytes, byteorder='big')) 43 | except OverflowError as e: 44 | raise OverflowError( 45 | "Could not store value into the specified number of bytes!") 46 | return bytes 47 | 48 | 49 | def bytes_to_val(bytes_lst: list) -> int: 50 | '''Merge n bytes into a value''' 51 | 52 | ''' 53 | bytes_to_val takes a list of bytes (values 0..255) and returns the value. Most values in DNS use 2 bytes, but you should implement a more generic algorithm to process a list of any length. 54 | ''' 55 | 56 | value = int.from_bytes(bytes_lst, byteorder="big") 57 | return value 58 | 59 | 60 | def get_left_bits(bytes_lst: list, n_bits: int) -> int: 61 | '''Extract left n bits of a two-byte sequence''' 62 | 63 | ''' 64 | get_left_bits takes a 2-byte list and a number n and returns leftmost n bits of that sequence as an integer. 65 | ''' 66 | 67 | b = bytes_to_val(bytes_lst) 68 | b = b >> (16-n_bits) 69 | return b 70 | 71 | 72 | def get_right_bits(bytes_lst: list, n_bits) -> int: 73 | '''Extract right n bits bits of a two-byte sequence''' 74 | 75 | ''' 76 | get_right_bits takes a 2-byte list and a number n and returns rightmost n bits of that sequence as an integer. 77 | ''' 78 | 79 | b = bytes_to_val(bytes_lst) 80 | b = b & int("1"*n_bits, 2) 81 | return b 82 | 83 | 84 | def read_zone_file(filename: str) -> tuple: 85 | '''Read the zone file and build a dictionary''' 86 | 87 | ''' 88 | read_zone_file takes file name as a parameter and reads the zone from that file. This function builds a dictionary of the following format: {domain: [(ttl, class, type, address)]} where each record is a list of tuples (answers). The function should return a tuple of (origin, zone_dict). If the requested domain is not in our zon, parse_request should raise a ValueError. Note that the records in the zone file may be incomplete (missing a domain name or TTL). The missing domain name should be replaced with the one from the previous line, missing TTL should be replaced with the default one (2nd line of the zone file). If a record contains multiple answers, return them all. 89 | ''' 90 | 91 | zone = dict() 92 | with open(filename) as zone_file: 93 | origin = zone_file.readline().split()[1].rstrip('.') 94 | origin_ttl = zone_file.readline().split()[1].rstrip('.') 95 | domain = "" 96 | for line in zone_file: 97 | line_lst = line.strip().split() 98 | if len(line_lst) == 5: 99 | domain = line_lst[0] 100 | zone[domain] = [(TTL_SEC[line_lst[1]], line_lst[2], line_lst[3], line_lst[4])] 101 | elif len(line_lst) == 4: 102 | if line_lst[0] in TTL_SEC: 103 | zone[domain].append((TTL_SEC[line_lst[0]], line_lst[1], line_lst[2], line_lst[3])) 104 | else: 105 | domain = line_lst[0] 106 | zone[domain] = [(TTL_SEC[origin_ttl], line_lst[1], line_lst[2], line_lst[3])] 107 | else: 108 | zone[domain].append((TTL_SEC[origin_ttl], line_lst[0], line_lst[1], line_lst[2])) 109 | 110 | return (origin, zone) 111 | 112 | 113 | def parse_request(origin: str, msg_req: bytes) -> tuple: 114 | '''Parse the request''' 115 | 116 | ''' 117 | parse_request takes origin and the request bytes and returns a tuple of (transaction id, domain, query type, query). The query is required as it is included in the response. This function must raise ValueErrors if the type, class, or zone (origin) cannot be processed. Those exceptions are caught in the run function. 118 | 119 | 56 f0 01 00 00 01 00 00 00 00 00 00 06 6c 75 74 68 65 72 03 65 64 75 00 00 01 00 01 120 | |---| |---| |---| |---| |---| |---| |------------------| |---------| || |---| |---| 121 | |id | |flags, # of questions etc | | luther | | edu | \0 |typ| |cls| 122 | |------------------query----------------------| 123 | ''' 124 | 125 | ''' 126 | assert parse_request('cs430.luther.edu', b'6\xc3\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') == \ 127 | (14019, 'ant', 1, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') 128 | 129 | assert parse_request('cs430.luther.edu', b'i\xce\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01') == \ 130 | (27086, 'ant', 28, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01') 131 | ''' 132 | 133 | transaction_id = int(msg_req[0:2].hex(), 16) 134 | domain_start = msg_req[12] 135 | domain = msg_req[13:13+domain_start].decode("utf-8") 136 | 137 | origin_file, _ = read_zone_file('zoo.zone') 138 | if origin != origin_file: 139 | raise ValueError("Unknown zone") 140 | 141 | query_type = int(msg_req[-4:-2].hex(), 16) 142 | if query_type not in [1, 28]: 143 | raise ValueError('Unknown query type') 144 | query = msg_req[12:] 145 | 146 | clas = int(msg_req[-2:].hex(), 16) 147 | if clas != 1: 148 | raise ValueError("Unknown class") 149 | 150 | print((transaction_id, domain, query_type, query)) 151 | return (transaction_id, domain, query_type, query) 152 | 153 | 154 | def format_response(zone: dict, trans_id: int, qry_name: str, qry_type: int, qry: bytearray) -> bytearray: 155 | '''Format the response''' 156 | 157 | ''' 158 | format_response takes the zone dictionary, transaction_id, domain name, and the query. It formats the DNS response (bytearray) based on those values and returns it to the calling function. Your should either label or pointer to format the domain name. 159 | ''' 160 | 161 | ''' 162 | assert format_response(self.zone, 4783, 'ant', 1, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') == \ 163 | b'\x12\xaf\x81\x00\x00\x01\x00\x02\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xb9T\xe0Y\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc7SC\x9e' 164 | 165 | assert format_response(self.zone, 55933, 'ant', 28, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01') == \ 166 | b'\xda}\x81\x00\x00\x01\x00\x01\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x0e\x10\x00\x10J\x9ap\xec:\xc0\xc6\x845\x9e\x8d7\x94\x86YY' 167 | ''' 168 | 169 | formatted_response = bytearray() 170 | 171 | trans_id_bytes = val_to_bytes(trans_id, 2) 172 | formatted_response.extend(trans_id_bytes) 173 | 174 | flag_bytes = b'\x81\x00' 175 | formatted_response.extend(flag_bytes) 176 | 177 | answers = zone[qry_name] 178 | num_of_answers = 0 179 | type_answers = [] 180 | for answer in answers: 181 | if answer[2] == DNS_TYPES[qry_type]: 182 | type_answers.append(answer) 183 | num_of_answers += 1 184 | 185 | other1 = b'\x00\x01' 186 | formatted_response.extend(other1) 187 | other2 = val_to_bytes(num_of_answers, 2) 188 | formatted_response.extend(other2) 189 | other3 = b'\x00\x00\x00\x00' 190 | formatted_response.extend(other3) 191 | 192 | formatted_response.extend(qry) 193 | 194 | ''' 195 | c0 0c 00 01 00 01 00 00 00 05 00 04 ae 81 19 aa 196 | |---| |---| |---| |---------| |---| |---------| 197 | |ptr| |typ| |cls| | ttl | |len| | address | 198 | 199 | {domain: [(ttl, class, type, address)]} 200 | ''' 201 | 202 | for answer in type_answers: 203 | formatted_response.extend(b'\xc0\x0c') 204 | formatted_response.extend(val_to_bytes(qry_type, 2)) 205 | formatted_response.extend(b'\x00\x01') 206 | formatted_response.extend(val_to_bytes(answer[0], 4)) 207 | if qry_type == 28: 208 | formatted_response.extend(val_to_bytes(16, 2)) 209 | for part in answer[3].split(':'): 210 | print(part) 211 | formatted_response.extend(val_to_bytes(int(part, 16), 2)) 212 | elif qry_type == 1: 213 | formatted_response.extend(val_to_bytes(4, 2)) 214 | for part in answer[3].split('.'): 215 | formatted_response.extend(val_to_bytes(int(part), 1)) 216 | else: 217 | raise ValueError('Unknown query type') 218 | 219 | return formatted_response 220 | 221 | 222 | def run(filename: str) -> None: 223 | '''Main server loop''' 224 | server_sckt = socket(AF_INET, SOCK_DGRAM) 225 | server_sckt.bind((HOST, PORT)) 226 | origin, zone = read_zone_file(filename) 227 | print("Listening on %s:%d" % (HOST, PORT)) 228 | 229 | while True: 230 | (request_msg, client_addr) = server_sckt.recvfrom(512) 231 | try: 232 | trans_id, domain, qry_type, qry = parse_request(origin, request_msg) 233 | msg_resp = format_response(zone, trans_id, domain, qry_type, qry) 234 | server_sckt.sendto(msg_resp, client_addr) 235 | except ValueError as ve: 236 | print('Ignoring the request: {}'.format(ve)) 237 | server_sckt.close() 238 | 239 | 240 | def main(*argv): 241 | '''Main function''' 242 | if len(argv[0]) != 2: 243 | print('Proper use: python3 nameserver.py ') 244 | exit() 245 | run(argv[0][1]) 246 | 247 | 248 | if __name__ == '__main__': 249 | main(sys.argv) 250 | -------------------------------------------------------------------------------- /4-DNS Server/output/IMG_20181008_120335654.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/4-DNS Server/output/IMG_20181008_120335654.jpg -------------------------------------------------------------------------------- /4-DNS Server/output/IMG_20181008_120343398_HDR.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/4-DNS Server/output/IMG_20181008_120343398_HDR.jpg -------------------------------------------------------------------------------- /4-DNS Server/output/IMG_20181008_120349203_HDR.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/4-DNS Server/output/IMG_20181008_120349203_HDR.jpg -------------------------------------------------------------------------------- /4-DNS Server/output/IMG_20181008_120400036.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/4-DNS Server/output/IMG_20181008_120400036.jpg -------------------------------------------------------------------------------- /4-DNS Server/test_nameserver.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Testing the DNS Server 3 | ''' 4 | #!/usr/bin/python3 5 | 6 | 7 | from random import seed 8 | import pytest 9 | from nameserver import val_to_bytes 10 | from nameserver import bytes_to_val 11 | from nameserver import get_left_bits 12 | from nameserver import get_right_bits 13 | from nameserver import read_zone_file 14 | from nameserver import parse_request 15 | from nameserver import format_response 16 | 17 | seed(430) 18 | 19 | 20 | class TestServer: 21 | '''Testing DNS server''' 22 | 23 | @pytest.fixture(scope='function', autouse=True) 24 | def setup_class(self): 25 | '''Setting up''' 26 | self.zone = read_zone_file('zoo.zone')[1] 27 | 28 | def test_val_to_bytes(self): 29 | '''Convert a value to bytes''' 30 | assert val_to_bytes(43043, 2) == [168, 35] 31 | assert val_to_bytes(430430, 3) == [6, 145, 94] 32 | 33 | def test_bytes_to_val(self): 34 | '''Convert bytes to a value''' 35 | assert bytes_to_val([145, 94]) == 37214 36 | assert bytes_to_val([6, 145, 94]) == 430430 37 | 38 | def test_get_left_bits(self): 39 | '''Get left bits''' 40 | assert get_left_bits([200, 100], 2) == 3 41 | assert get_left_bits([200, 100], 4) == 12 42 | 43 | def test_get_right_bits(self): 44 | '''Get right bits''' 45 | assert get_right_bits([200, 100], 14) == 2148 46 | assert get_right_bits([200, 100], 6) == 36 47 | 48 | def test_read_zone_file(self): 49 | '''Read the zone file''' 50 | origin, zone = read_zone_file('zoo.zone') 51 | assert origin == 'cs430.luther.edu' 52 | assert len(zone) == 25 53 | 54 | def test_format_response(self): 55 | '''Format a response''' 56 | assert format_response(self.zone, 4783, 'ant', 1, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') == \ 57 | b'\x12\xaf\x81\x00\x00\x01\x00\x02\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xb9T\xe0Y\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc7SC\x9e' 58 | 59 | assert format_response(self.zone, 55933, 'ant', 28, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01') == \ 60 | b'\xda}\x81\x00\x00\x01\x00\x01\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01\xc0\x0c\x00\x1c\x00\x01\x00\x00\x0e\x10\x00\x10J\x9ap\xec:\xc0\xc6\x845\x9e\x8d7\x94\x86YY' 61 | 62 | def test_parse_request(self): 63 | '''Parse the request''' 64 | assert parse_request('cs430.luther.edu', b'6\xc3\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') == \ 65 | (14019, 'ant', 1, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') 66 | 67 | assert parse_request('cs430.luther.edu', b'i\xce\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01') == \ 68 | (27086, 'ant', 28, b'\x03ant\x05cs430\x06luther\x03edu\x00\x00\x1c\x00\x01') 69 | 70 | def test_parse_request_query_error(self): 71 | '''Query type is incorrect''' 72 | with pytest.raises(ValueError) as excinfo: 73 | parse_request('cs430.luther.edu', b'6\xc3\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x03\x00\x01') 74 | exception_msg = excinfo.value.args[0] 75 | assert exception_msg == 'Unknown query type' 76 | 77 | def test_parse_request_class_error(self): 78 | '''Query class is incorrect''' 79 | with pytest.raises(ValueError) as excinfo: 80 | parse_request('cs430.luther.edu', b'6\xc3\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x03') 81 | exception_msg = excinfo.value.args[0] 82 | assert exception_msg == 'Unknown class' 83 | 84 | def test_parse_request_query_zone(self): 85 | '''Query zone is incorrect''' 86 | with pytest.raises(ValueError) as excinfo: 87 | parse_request('luther.edu', b'6\xc3\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03ant\x05cs430\x06luther\x03edu\x00\x00\x01\x00\x01') 88 | exception_msg = excinfo.value.args[0] 89 | assert exception_msg == 'Unknown zone' 90 | 91 | if __name__ == '__main__': 92 | pytest.main(['test_nameserver.py']) 93 | -------------------------------------------------------------------------------- /4-DNS Server/zoo.zone: -------------------------------------------------------------------------------- 1 | $ORIGIN cs430.luther.edu. 2 | $TTL 1m 3 | emu 1w IN A 70.15.79.109 4 | 1m IN A 157.96.36.98 5 | 1m IN AAAA cfd0:486c:21a9:7edd:9269:5856:9a6e:52e1 6 | duck 1w IN A 30.237.15.163 7 | 1w IN AAAA 4491:cfc:eee:8bf9:d42b:521d:6b1d:caf 8 | viper 1y IN A 54.118.84.148 9 | 1y IN AAAA 97f2:cebe:454a:496:3d4c:f64d:2950:3d42 10 | ant 1h IN A 185.84.224.89 11 | 1h IN A 199.83.67.158 12 | 1h IN AAAA 4a9a:70ec:3ac0:c684:359e:8d37:9486:5959 13 | hawk 1w IN A 90.103.161.91 14 | 1w IN AAAA 2216:793a:9f8d:5237:3b18:61ab:212f:5738 15 | bear 1w IN A 241.152.243.10 16 | 1w IN AAAA b82e:40f2:4a7f:466b:efd0:3654:f454:25b9 17 | cat 1d IN A 34.28.140.109 18 | 1d IN AAAA f76b:c394:ed45:4464:3915:822c:1390:4682 19 | zebra 1w IN A 124.25.11.229 20 | 1w IN AAAA 93c8:5a1d:45e0:8be8:27bc:6659:1dc6:ed27 21 | lion 1m IN A 210.249.68.68 22 | IN A 145.163.1.144 23 | IN AAAA e1b8:c223:8c44:53b0:6c6d:6a56:5cd:867b 24 | oryx 1d IN A 66.244.135.103 25 | 1d IN AAAA c085:3375:ff16:8a70:5cac:f6a8:5f76:8988 26 | seal 1y IN A 172.198.93.55 27 | 1y IN AAAA c672:42b1:45dd:5eda:2b2b:bc6:cca7:946d 28 | kudu 1y IN A 199.157.135.217 29 | 1y IN AAAA 99c9:9e2f:5cbb:f22d:7b97:7a3d:1087:a955 30 | urial 1w IN A 245.84.169.153 31 | 1y IN A 241.81.245.84 32 | 1y IN AAAA 1d77:1069:8f62:7bb1:c830:cf8d:3b5:376d 33 | rat IN A 116.102.240.67 34 | 1h IN A 40.162.130.241 35 | 1h IN AAAA 2bb5:8bc8:4d6:b39:5186:755:3392:f5ff 36 | ibis 1s IN A 110.23.32.132 37 | 1s IN AAAA 955b:aca9:7b54:7248:433a:e12b:6697:99d0 38 | yak 1y IN A 75.191.54.34 39 | 1y IN AAAA f68e:56bb:fd53:8176:4bf:25c8:c537:81e5 40 | topi 1m IN A 86.96.30.194 41 | 1m IN AAAA 12ad:349d:a28:6836:767a:51b4:aa21:3120 42 | wolf 1y IN A 93.187.49.252 43 | 1y IN AAAA 7e6c:f398:6c4:432c:a5eb:b080:71b9:eba9 44 | mara 1m IN A 160.159.185.33 45 | 1m IN AAAA 1039:551c:4c43:bfc3:4733:5d81:73e4:c9a5 46 | puma IN A 70.84.137.68 47 | IN A 60.168.65.2 48 | IN AAAA 774c:b954:7e7b:5b3:1e9d:fe7b:7c3f:68a6 49 | quail IN A 228.55.207.204 50 | IN AAAA 15db:d0d3:ca94:1491:88d4:8bc7:e762:41ac 51 | fox 1h IN A 192.243.47.158 52 | 1h IN AAAA 28f3:4f3b:c12a:65a5:d9fe:e7dc:dd64:c124 53 | gaur 1y IN A 20.164.186.120 54 | 1y IN AAAA b753:d7a:9016:e948:b2a8:015:7523:43d2 55 | jaguar IN A 50.22.17.142 56 | IN AAAA 9ac7:3837:307f:82dd:6a17:ed19:34ba:ebf1 57 | nyala 1d IN A 16.37.65.66 58 | 1d IN AAAA 05:2c96:d58d:d422:8fa4:da2c:2445:c231 59 | -------------------------------------------------------------------------------- /5-Web Server/README.md: -------------------------------------------------------------------------------- 1 | # Simple Web Server 2 | 3 | For this project you are going to write a simple web server using Python sockets only (i.e. no Flask, not even http.server). Your server should have the following functionality: 4 | 5 | 1. Bind to TCP port **4300** on **127.0.0.2** and accept 1 request at a time. 6 | 2. Serve a single file, *alice30.txt*. 7 | 3. Log details of each incoming request to *webserver.log*. 8 | 4. Return *405 Method Not Allowed* error for any method other than *GET* 9 | 5. Return *404 Not Found* error for any request other than */alice30.txt*. 10 | 6. Send the content of *alice30.txt* to the client along with proper response header. 11 | 12 | ## Request 13 | 14 | A typical request header sent by a browser (Chrome in this case) looks as follows: 15 | 16 | ``` 17 | GET /alice30.txt HTTP/1.1 18 | Host: 127.0.0.2:4300 19 | Connection: keep-alive 20 | Pragma: no-cache 21 | Cache-Control: no-cache 22 | Upgrade-Insecure-Requests: 1 23 | User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 24 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 25 | Accept-Encoding: gzip, deflate, br 26 | Accept-Language: en-US,en;q=0.9 27 | 28 | ``` 29 | 30 | Use `curl` to initiate a *POST* request and see a 405 error: 31 | 32 | ``` 33 | curl -X POST http://127.0.0.2:4300/alice30.txt 34 | ``` 35 | 36 | ## Log 37 | 38 | Log file should contain the following information: 39 | 40 | 1. Time of the request. 41 | 2. Requested file. 42 | 3. IP address of the client. 43 | 4. Browser vendor and version. 44 | 45 | ``` 46 | 2018-11-05 09:10:52.906984 | /alice.txt | 127.0.0.1 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 47 | 2018-11-05 09:12:18.072352 | /alice30.txt | 127.0.0.1 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 48 | 2018-11-05 09:15:00.526359 | /alice.txt | 127.0.0.1 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 49 | 2018-11-05 09:21:58.869701 | /alice.txt | 127.0.0.1 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 50 | 2018-11-05 09:22:06.622828 | /alice30.txt | 127.0.0.1 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 51 | ``` 52 | 53 | ## Response 54 | 55 | Your server must include the following headers in the response: 56 | 57 | 1. HTTP version: 1.1 58 | 2. Response code: 200 OK 59 | 3. `Content-Length`: length of *alice30.txt*. 60 | 4. `Content-Type`: plain text (**not** html). 61 | 5. `Date`: current date 62 | 6. `Last-Modified`: Friday, August 29, 2018 11:00 AM 63 | 7. `Server`: must include **your name** 64 | 65 | The response header sent by your server should look as follows (date and server are going to be different): 66 | 67 | ``` 68 | HTTP/1.1 200 OK 69 | Content-Length: 148545 70 | Content-Type: text/plain; charset=utf-8 71 | Date: Sun Nov 4 23:25:40 2018 72 | Last-Modified: Wed Aug 29 11:00:00 2018 73 | Server: CS430-ROMAN 74 | 75 | ``` 76 | 77 | ## References 78 | 79 | * [RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content](https://tools.ietf.org/html/rfc7231) 80 | 81 | * [Request header - MDN Web Docs Glossary: Definitions of Web-related terms | MDN](https://developer.mozilla.org/en-US/docs/Glossary/Request_header) 82 | 83 | * [Response header - MDN Web Docs Glossary: Definitions of Web-related terms | MDN](https://developer.mozilla.org/en-US/docs/Glossary/Response_header) 84 | 85 | * [Alice's Adventures in Wonderland](www.umich.edu/~umfandsf/other/ebooks/alice30.txt) 86 | -------------------------------------------------------------------------------- /5-Web Server/webserver.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/5-Web Server/webserver.pcapng -------------------------------------------------------------------------------- /5-Web Server/webserver.py: -------------------------------------------------------------------------------- 1 | """Python Web server implementation""" 2 | from socket import socket, AF_INET, SOCK_STREAM 3 | from datetime import datetime 4 | 5 | server = socket(AF_INET, SOCK_STREAM) 6 | 7 | ADDRESS = "127.0.0.2" # Local client is going to be 127.0.0.1 8 | PORT = 4300 # Open http://127.0.0.2:4300 in a browser 9 | LOGFILE = "webserver.log" 10 | 11 | def read_file(filename: str) -> str: 12 | '''Read file and return String object''' 13 | strObj = "" 14 | 15 | with open(filename, 'r') as f: 16 | strObj = f.read() 17 | 18 | return strObj 19 | 20 | def serve(strObj: str, request_type: str, request_uri: str) -> None: 21 | '''Serving content''' 22 | 23 | server.bind((ADDRESS, PORT)) 24 | server.listen(1) 25 | 26 | with server: 27 | # Server loop - this will keep the server going until we close it ourselves! 28 | while True: 29 | conn, addr = server.accept() 30 | print("Connection Opened...") 31 | with conn: 32 | data = conn.recv(1024) 33 | if not data: 34 | print("Connection Closed.") 35 | continue 36 | 37 | # Getting request decoded 38 | request = data.decode() 39 | r_lst = request.splitlines() 40 | query = r_lst[0].split() 41 | qtype, quri = query[0], query[1] 42 | r_lst = [s.split(": ") for s in r_lst[1:]] 43 | r_dct = {} 44 | for element in r_lst: 45 | try: 46 | r_dct[element[0]] = element[1] 47 | except: 48 | continue 49 | 50 | rsp = [] 51 | 52 | with open(LOGFILE, "a+") as f: 53 | f.write(str(datetime.isoformat(datetime.now()).replace('T', ' ')) + \ 54 | " | " + quri + " | " + str(addr[0]) + " | " + r_dct["User-Agent"] + '\n') 55 | 56 | # Checking for errors and encoding the sent data 57 | error = True 58 | if qtype != request_type: 59 | msg = "405 Method Not Allowed\n" 60 | strE = msg.encode() 61 | rsp.append("HTTP/1.1 405 Method Not Allowed") 62 | elif quri != request_uri: 63 | msg = "404 Not Found" 64 | strE = msg.encode() 65 | rsp.append("HTTP/1.1 404 Not Found") 66 | else: 67 | error = False 68 | strE = strObj.encode() 69 | rsp.append("HTTP/1.1 200 OK") 70 | 71 | # Building response header 72 | rsp.append(("Content-Length: " + str(len(strE)))) 73 | rsp.append("Content-Type: text/plain; charset=utf-8") 74 | rsp.append(("Date: " + datetime.now().strftime("%c"))) 75 | if error: 76 | rsp.append("Last-Modified: " + datetime.now().strftime("%c")) 77 | else: 78 | rsp.append("Last-Modified: Wed Aug 29 11:00:00 2018") 79 | rsp.append("Server: CS430-Ahmad M. Osman") 80 | rsp.append("\n") 81 | rsp = '\n'.join(rsp) 82 | 83 | # try/except in case of a broken pipe! 84 | try: 85 | conn.sendall(rsp.encode()) 86 | conn.sendall(strE) 87 | except: 88 | pass 89 | print("Connection Closed.") 90 | 91 | def alice(): 92 | """Serve Alice in Wonderland""" 93 | alice = read_file("alice30.txt") 94 | serve(alice, "GET", "/alice30.txt") 95 | 96 | 97 | def main(): 98 | """Main server loop""" 99 | alice() # Serving Alice! 100 | 101 | 102 | if __name__ == "__main__": 103 | main() 104 | -------------------------------------------------------------------------------- /5-Web Server/webserver_404.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/5-Web Server/webserver_404.pcapng -------------------------------------------------------------------------------- /6-Ping Tool/README.md: -------------------------------------------------------------------------------- 1 | # The Story about Ping 2 | 3 | Implement a *ping* application using ICMP request and reply messages. You have to use Python's *RAW* sockets to properly send ICMP messages. You should complete the provided ping application so that it sends 5 Echo requests to each RIR (ARIN, RIPE, LACNIC, AFRINIC, APNIC), 1 request every second. Each message contains a payload of data that includes a timestamp. After sending each packet, the application waits up to one second to receive a reply. If the one-second timer expires, assume the packet is lost or the server is down. Report the following statistics for each host: 4 | 5 | * packet loss 6 | * maximum round-trip time 7 | * minimum RTT 8 | * average RTT 9 | * RTT standard deviation 10 | 11 | ## Code notes 12 | 13 | You need to receive the structure ICMP_ECHO_REPLY and fetch the information you need, such as checksum, sequence number, time to live (TTL), etc. 14 | 15 | This application requires the use of raw sockets. You may need administrator/root privileges to run your program. 16 | 17 | When running this application as a root, make sure to use `python3`. 18 | 19 | ### Functions 20 | 21 | * `print_raw_bytes`: auxiliary function, useful for debugging. Takes (received) *packet bytes* as an argument. **Can be used without modifications** 22 | 23 | * `checksum`: calculates packet checksum. Takes *packet bytes* as an argument. **Can be used without modifications** 24 | 25 | * `parse_reply`: receives and parses an echo reply. Takes the following arguments: *socket*, *request id*, *timeout*, and the *destination address*. Returns a tuple of the *destination address*, *packet size*, *roundtrip time*, *time to live*, and *sequence number*. You need to modify lines between labels **TODO** and **DONE**. This function should raise an error if the response message type, code, or checksum are incorrect. 26 | 27 | * `format_request`: formats echo request. Takes *request id* and *sequence number* as arguments. **Can be used without modifications** 28 | 29 | 0 || 15 || 30 | ---|---|---|--- 31 | type | code | checksum 32 | id || sequence 33 | 34 | 35 | * `send_request`: creates a socket and uses sends a message prepared by `format_request`. **Can be used without modifications** 36 | 37 | * `ping`: main loop. Takes a *destination host*, *number of packets to send*, and *timeout* as arguments. Displays host statistics. You need to modify lines between labels **TODO** and **DONE**. 38 | 39 | ## References 40 | 41 | * [socket — Low-level networking interface — Python 3.7.1 documentation](https://docs.python.org/3/library/socket.html) 42 | 43 | * [https://sock-raw.org/papers/sock_raw](https://sock-raw.org/papers/sock_raw) 44 | 45 | * [TCP/IP Raw Sockets | Microsoft Docs](https://docs.microsoft.com/en-us/windows/desktop/WinSock/tcp-ip-raw-sockets-2) 46 | 47 | * [Converting between Structs and Byte Arrays – GameDev](http://genericgamedev.com/general/converting-between-structs-and-byte-arrays/) 48 | 49 | * [select — Waiting for I/O completion — Python 3.7.1 documentation](https://docs.python.org/3/library/select.html) 50 | 51 | * [How to Work with TCP Sockets in Python (with Select Example)](https://steelkiwi.com/blog/working-tcp-sockets/) 52 | 53 | * [select — Wait for I/O Efficiently — PyMOTW 3](https://pymotw.com/3/select/) 54 | -------------------------------------------------------------------------------- /6-Ping Tool/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/6-Ping Tool/__init__.py -------------------------------------------------------------------------------- /6-Ping Tool/pinger.py: -------------------------------------------------------------------------------- 1 | """Python Pinger""" 2 | #!/usr/bin/env python3 3 | # encoding: UTF-8 4 | 5 | import binascii 6 | import os 7 | import select 8 | import struct 9 | import sys 10 | import time 11 | import socket 12 | from statistics import mean, stdev 13 | 14 | ECHO_REQUEST_TYPE = 8 15 | ECHO_REPLY_TYPE = 0 16 | ECHO_REQUEST_CODE = 0 17 | ECHO_REPLY_CODE = 0 18 | REGISTRARS = ["afrinic.net", "apnic.net", "arin.net", "lacnic.net", "ripe.net"] 19 | # REGISTRARS = ["example.com"] 20 | 21 | 22 | def print_raw_bytes(pkt: bytes) -> None: 23 | """Printing the packet bytes""" 24 | for i in range(len(pkt)): 25 | sys.stdout.write("{:02x} ".format(pkt[i])) 26 | if (i + 1) % 16 == 0: 27 | sys.stdout.write("\n") 28 | elif (i + 1) % 8 == 0: 29 | sys.stdout.write(" ") 30 | sys.stdout.write("\n") 31 | 32 | 33 | def checksum(pkt: bytes) -> int: 34 | """Calculate checksum""" 35 | csum = 0 36 | count = 0 37 | count_to = (len(pkt) // 2) * 2 38 | 39 | while count < count_to: 40 | this_val = (pkt[count + 1]) * 256 + (pkt[count]) 41 | csum = csum + this_val 42 | csum = csum & 0xFFFFFFFF 43 | count = count + 2 44 | 45 | if count_to < len(pkt): 46 | csum = csum + (pkt[len(pkt) - 1]) 47 | csum = csum & 0xFFFFFFFF 48 | 49 | csum = (csum >> 16) + (csum & 0xFFFF) 50 | csum = csum + (csum >> 16) 51 | result = ~csum 52 | result = result & 0xFFFF 53 | result = result >> 8 | (result << 8 & 0xFF00) 54 | 55 | return result 56 | 57 | 58 | def parse_reply(my_socket: socket.socket, req_id: int, timeout: int, addr_dst: str) -> tuple: 59 | """ 60 | Receive an Echo reply and parses it. 61 | Takes the following arguments: socket, request id, timeout, and the destination address. 62 | Returns a tuple of the destination address, packet size, roundtrip time, time to live, and sequence number. 63 | You need to modify lines between labels TODO and DONE. 64 | This function should raise an error if the response message type, code, or checksum are incorrect. 65 | """ 66 | time_left = timeout 67 | while True: 68 | started_select = time.time() 69 | what_ready = select.select([my_socket], [], [], time_left) 70 | how_long_in_select = time.time() - started_select 71 | if what_ready[0] == []: # Timeout 72 | raise TimeoutError("Request timed out after 1 sec") 73 | 74 | time_rcvd = time.time() 75 | pkt_rcvd, addr = my_socket.recvfrom(1024) 76 | if addr[0] != addr_dst: 77 | raise ValueError(f"Wrong sender: {addr[0]}") 78 | 79 | # TODO: Extract ICMP header from the IP packet and parse it 80 | 81 | # print_raw_bytes(pkt_rcvd) 82 | 83 | pkt_size = len(pkt_rcvd) 84 | ICMP_header = pkt_rcvd[20:28] 85 | ICMP_type, code, chksum, pkt_id, seq_num = struct.unpack("bbHHh", ICMP_header) 86 | 87 | # print_raw_bytes(ICMP_header) 88 | # print_raw_bytes(struct.unpack("bbHHh", ICMP_header)) 89 | 90 | if ICMP_type != ECHO_REPLY_TYPE: 91 | raise ValueError(f"Wrong ICMP Type: {ICMP_type}") 92 | elif code != ECHO_REPLY_CODE: 93 | raise ValueError(f"Wrong Code Type: {code}") 94 | elif pkt_id != req_id: 95 | raise ValueError("Request ID is not the same as the Packet ID") 96 | 97 | bts = struct.calcsize("d") 98 | timestamp = struct.unpack("d", pkt_rcvd[28:28 + bts])[0] 99 | rtt = time_rcvd - timestamp 100 | 101 | ttl = pkt_rcvd[8] 102 | 103 | my_checksum = 0 104 | hdr = struct.pack("bbHHh", ICMP_type, code, my_checksum, pkt_id, seq_num) 105 | my_checksum = checksum(hdr + pkt_rcvd[28:]) 106 | 107 | v = list(my_checksum.to_bytes(2, byteorder='big')) 108 | my_hex_chksum = hex(v[0]).replace("0x", '') + hex(v[1]).replace("0x", '') 109 | 110 | chksum_raw_bytes = pkt_rcvd[22:24] 111 | 112 | tmp = [] 113 | for i in range(len(chksum_raw_bytes)): 114 | tmp.append("{:02x} ".format(chksum_raw_bytes[i])) 115 | 116 | tmp = ''.join(tmp) 117 | tmp = tmp.replace(' ', '') 118 | # print(tmp) 119 | 120 | if my_hex_chksum != tmp: 121 | raise ValueError("Incorrect Checksum") 122 | 123 | # DONE: End of ICMP parsing 124 | 125 | time_left = time_left - how_long_in_select 126 | if time_left <= 0: 127 | raise TimeoutError("Request timed out after 1 sec") 128 | 129 | return (addr_dst, pkt_size, rtt, ttl, seq_num) 130 | 131 | def format_request(req_id: int, seq_num: int) -> bytes: 132 | """Format an Echo request""" 133 | my_checksum = 0 134 | header = struct.pack( 135 | "bbHHh", ECHO_REQUEST_TYPE, ECHO_REQUEST_CODE, my_checksum, req_id, seq_num 136 | ) 137 | data = struct.pack("d", time.time()) 138 | my_checksum = checksum(header + data) 139 | 140 | if sys.platform == "darwin": 141 | my_checksum = socket.htons(my_checksum) & 0xFFFF 142 | else: 143 | my_checksum = socket.htons(my_checksum) 144 | 145 | header = struct.pack( 146 | "bbHHh", ECHO_REQUEST_TYPE, ECHO_REQUEST_CODE, my_checksum, req_id, seq_num 147 | ) 148 | packet = header + data 149 | return packet 150 | 151 | 152 | def send_request(addr_dst: str, seq_num: int, timeout: int = 1) -> tuple: 153 | """Send an Echo Request""" 154 | result = None 155 | proto = socket.getprotobyname("icmp") 156 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, proto) 157 | my_id = os.getpid() & 0xFFFF 158 | 159 | packet = format_request(my_id, seq_num) 160 | my_socket.sendto(packet, (addr_dst, 1)) 161 | 162 | try: 163 | result = parse_reply(my_socket, my_id, timeout, addr_dst) 164 | except ValueError as ve: 165 | raise(f"Packet error: {ve}") 166 | finally: 167 | my_socket.close() 168 | return result 169 | 170 | 171 | def ping(host: str, pkts: int, timeout: int = 1) -> None: 172 | """ 173 | Main loop 174 | Takes a destination host, number of packets to send, and timeout as arguments. 175 | Displays host statistics. You need to modify lines between labels TODO and DONE. 176 | """ 177 | # TODO: Implement the main loop 178 | addr_dst = socket.gethostbyname(host) 179 | 180 | print() 181 | print("--- Ping", host, '(' + addr_dst + ')', "using Python ---") 182 | print() 183 | 184 | pkts_lost = 0 185 | rttlst = [] 186 | for i in range(pkts): 187 | result = "" 188 | try: 189 | result = send_request(addr_dst, seq_num = i, timeout = timeout) 190 | rttlst.append(round(result[2] * pow(10, 3), 2)) 191 | print(result[1], "bytes from", str(result[0]) + ": icmm_seq=" + str(result[4] + 1), "TTL=" + str(result[3]), "time=" + str(round(result[2] * pow(10, 3), 2)), "ms") 192 | except TimeoutError as e: 193 | result = "No response: " + str(e) 194 | pkts_lost += 1 195 | print(result) 196 | except Exception as e: 197 | pkts_lost += 1 198 | print("Error:", e) 199 | 200 | print() 201 | print("---", host, "ping statistics ---") 202 | 203 | if pkts_lost == pkts: 204 | print(pkts, "packets transmitted,", pkts-pkts_lost, "received, 100% packet loss") 205 | else: 206 | print(pkts, "packets transmitted,", pkts-pkts_lost, "received,", str(int(pkts_lost/pkts * 100)) + "% packet loss") 207 | try: 208 | rttMin = round(min(rttlst), 3) 209 | rttMax = round(max(rttlst), 3) 210 | rttAvg = round(mean(rttlst), 3) 211 | rttStdev = round(stdev(rttlst), 3) 212 | print(f"rtt min/avg/max/mdev = {rttMin}/{rttAvg}/{rttMax}/{rttStdev} ms") 213 | except: 214 | pass 215 | # DONE 216 | return 217 | 218 | 219 | if __name__ == "__main__": 220 | for rir in REGISTRARS: 221 | ping(rir, 5) 222 | -------------------------------------------------------------------------------- /6-Ping Tool/pinger_output.txt: -------------------------------------------------------------------------------- 1 | 2 | --- Ping afrinic.net (196.216.2.6) using Python --- 3 | 4 | 36 bytes from 196.216.2.6: icmp_seq=1 TTL=128 time=257.80 ms 5 | 36 bytes from 196.216.2.6: icmp_seq=2 TTL=128 time=276.89 ms 6 | 36 bytes from 196.216.2.6: icmp_seq=3 TTL=128 time=330.03 ms 7 | 36 bytes from 196.216.2.6: icmp_seq=4 TTL=128 time=327.21 ms 8 | 36 bytes from 196.216.2.6: icmp_seq=5 TTL=128 time=258.53 ms 9 | 10 | --- afrinic.net ping statistics --- 11 | 5 packets transmitted, 5 received, 0% packet loss 12 | rtt min/avg/max/mdev = 257.803/290.093/330.027/36.005 ms 13 | 14 | --- Ping apnic.net (203.119.101.61) using Python --- 15 | 16 | 36 bytes from 203.119.101.61: icmp_seq=1 TTL=128 time=294.96 ms 17 | 36 bytes from 203.119.101.61: icmp_seq=2 TTL=128 time=327.19 ms 18 | 36 bytes from 203.119.101.61: icmp_seq=3 TTL=128 time=221.30 ms 19 | 36 bytes from 203.119.101.61: icmp_seq=4 TTL=128 time=232.25 ms 20 | 36 bytes from 203.119.101.61: icmp_seq=5 TTL=128 time=227.27 ms 21 | 22 | --- apnic.net ping statistics --- 23 | 5 packets transmitted, 5 received, 0% packet loss 24 | rtt min/avg/max/mdev = 221.300/260.596/327.190/47.628 ms 25 | 26 | --- Ping arin.net (199.43.0.43) using Python --- 27 | 28 | 36 bytes from 199.43.0.43: icmp_seq=1 TTL=128 time=39.32 ms 29 | 36 bytes from 199.43.0.43: icmp_seq=2 TTL=128 time=40.75 ms 30 | 36 bytes from 199.43.0.43: icmp_seq=3 TTL=128 time=40.33 ms 31 | 36 bytes from 199.43.0.43: icmp_seq=4 TTL=128 time=40.35 ms 32 | 36 bytes from 199.43.0.43: icmp_seq=5 TTL=128 time=40.57 ms 33 | 34 | --- arin.net ping statistics --- 35 | 5 packets transmitted, 5 received, 0% packet loss 36 | rtt min/avg/max/mdev = 39.317/40.263/40.748/0.556 ms 37 | 38 | --- Ping lacnic.net (200.3.14.10) using Python --- 39 | 40 | 36 bytes from 200.3.14.10: icmp_seq=1 TTL=128 time=170.82 ms 41 | 36 bytes from 200.3.14.10: icmp_seq=2 TTL=128 time=205.16 ms 42 | 36 bytes from 200.3.14.10: icmp_seq=3 TTL=128 time=188.67 ms 43 | 36 bytes from 200.3.14.10: icmp_seq=4 TTL=128 time=164.20 ms 44 | 36 bytes from 200.3.14.10: icmp_seq=5 TTL=128 time=158.64 ms 45 | 46 | --- lacnic.net ping statistics --- 47 | 5 packets transmitted, 5 received, 0% packet loss 48 | rtt min/avg/max/mdev = 158.644/177.499/205.155/19.149 ms 49 | 50 | --- Ping ripe.net (193.0.6.139) using Python --- 51 | 52 | No response: Request timed out after 1 sec 53 | No response: Request timed out after 1 sec 54 | No response: Request timed out after 1 sec 55 | No response: Request timed out after 1 sec 56 | No response: Request timed out after 1 sec 57 | 58 | --- ripe.net ping statistics --- 59 | 5 packets transmitted, 0 received, 100% packet loss 60 | -------------------------------------------------------------------------------- /7-Traceroute Tool/README.md: -------------------------------------------------------------------------------- 1 | # Tracing the Route 2 | 3 | Implement `traceroute` utility. 4 | 5 | Language of implementation: any. 6 | 7 | Use UDP (preferred) or ICMP (acceptable) socket to send probing messages to the specified host. 8 | 9 | Display relevant statistics of the probe. 10 | 11 | For you convenience, lines of Python implementation are provided. 12 | 13 | ## C++ 14 | 15 | ``` 16 | g++ --std=gnu++14 traceroute.cpp -o traceroute.out 17 | ./traceroute.out example.com 18 | ``` 19 | 20 | ## Java 21 | 22 | ``` 23 | javac Traceroute.java 24 | java Traceroute example.com 25 | ``` 26 | 27 | ## Python 28 | 29 | ``` 30 | python3 traceroute.py example.com 31 | ``` 32 | 33 | ### Functions 34 | 35 | * `print_raw_bytes`: takes *packet* as an argument and prints prints all the bytes as hexadecimal values. 36 | 37 | * `checksum`: takes *packet* as an argument and returns its Internet **checksum**. 38 | 39 | * `format_request`: takes *ICMP type*, *ICMP code*, *request ID*, and *sequence number* as arguments and returns a properly formatted **ICMP request packet** with current time as *data*. This function has to compute the packet's *checksum* and add it to the header of the outgoing message. 40 | 41 | * `send_request`: takes *packet* bytes, *destination address*, and *Time-to-Live* value as arguments and returns a new **raw socket**. This function sets the socket's time-to-live option to the supplied value. 42 | 43 | * `receive_reply`: takes a *socket* and *timeout* as arguments and returns a tuple of the **received packet** and the **IP address** of the responding host. This function uses `select` and may raise a `TimeoutError` is the response does not come soon enough. 44 | 45 | * `parse_reply`: takes a *packet* as an argument and returns `True` if it is a valid (expected) response. This function parses the response header and verifies that the *ICMP type* is 0, 3, or 11. It also validates the response checksum and raises a `ValueError` if it's incorrect. 46 | 47 | * `traceroute`: takes *host* (domain) name as an argument and traces a path to that host. The general approach is to have a big loop that sends *ICMP Echo Request* messages to the host, incrementally increasing TTL value. Each iteration of this loop generates ATTEMPTS (3) messages. There are two possible sources of errors: `Timeout` (response was not received within **timeout**) and `Value` (something is wrong with the response). For each attempts you should do the following: 48 | 1. Format an ICMP Request 49 | 2. Send the request to the destination host 50 | 3. Receive a response (may or may not be a proper ICMP Reply) 51 | 4. Parse the response and check for errors 52 | 5. Print the relevant statistics, if possible 53 | 6. Print the error message, if any 54 | 7. Stop the probe after MAX_HOPS attempts or once a response from the destination host is received 55 | 56 | * `main`: takes command line arguments and starts the probe. 57 | 58 | 59 | ## References 60 | 61 | * [Traceroute - Wikipedia](https://en.wikipedia.org/wiki/Traceroute) 62 | 63 | * [traceroute(8) - Linux man page](https://linux.die.net/man/8/traceroute) 64 | 65 | * [Linux Howtos: C/C++ -> Sockets Tutorial](http://www.linuxhowtos.org/C_C++/socket.htm) 66 | 67 | * [Socket (Java SE 10 & JDK 10 )](https://docs.oracle.com/javase/10/docs/api/java/net/Socket.html) 68 | 69 | * [socket — Low-level networking interface — Python 3.7.1 documentation](https://docs.python.org/3/library/socket.html) 70 | -------------------------------------------------------------------------------- /7-Traceroute Tool/Traceroute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Java traceroute implementation 3 | */ 4 | 5 | class Traceroute { 6 | public static void main(String[] args) { 7 | System.out.println(String.format("Hello %s", args[0])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * C++ traceroute implementation 3 | */ 4 | #include "bits/stdc++.h" 5 | using namespace std; 6 | 7 | int main(int argc, char** argv) { 8 | cout << "Hello " << argv[1] << endl; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute.py: -------------------------------------------------------------------------------- 1 | """Python traceroute implementation""" 2 | #!/usr/bin/env python3 3 | # encoding: UTF-8 4 | 5 | 6 | import os 7 | import select 8 | import socket 9 | import struct 10 | import sys 11 | import time 12 | 13 | 14 | ATTEMPTS = 3 15 | ECHO_REQUEST_CODE = 0 16 | ECHO_REQUEST_TYPE = 8 17 | MAX_HOPS = 30 18 | TIMEOUT = 1 19 | 20 | 21 | def print_raw_bytes(pkt: bytes) -> None: 22 | """ 23 | Print the packet bytes. 24 | Takes packet as an argument and prints prints all the bytes as hexadecimal values. 25 | """ 26 | for i in range(len(pkt)): 27 | sys.stdout.write("{:02x} ".format(pkt[i])) 28 | if (i + 1) % 16 == 0: 29 | sys.stdout.write("\n") 30 | elif (i + 1) % 8 == 0: 31 | sys.stdout.write(" ") 32 | sys.stdout.write("\n") 33 | 34 | 35 | def checksum(pkt: bytes) -> int: 36 | """ 37 | Calculate and return checksum. 38 | Takes packet as an argument and returns its Internet checksum. 39 | """ 40 | csum = 0 41 | count = 0 42 | count_to = (len(pkt) // 2) * 2 43 | 44 | while count < count_to: 45 | this_val = (pkt[count + 1]) * 256 + (pkt[count]) 46 | csum = csum + this_val 47 | csum = csum & 0xFFFFFFFF 48 | count = count + 2 49 | 50 | if count_to < len(pkt): 51 | csum = csum + (pkt[len(pkt) - 1]) 52 | csum = csum & 0xFFFFFFFF 53 | 54 | csum = (csum >> 16) + (csum & 0xFFFF) 55 | csum = csum + (csum >> 16) 56 | result = ~csum 57 | result = result & 0xFFFF 58 | result = result >> 8 | (result << 8 & 0xFF00) 59 | 60 | return result 61 | 62 | 63 | def format_request(icmp_type: int, icmp_code: int, req_id: int, seq_num: int) -> bytes: 64 | """ 65 | Format an Echo request. 66 | Takes ICMP type, ICMP code, request ID, and sequence number as arguments and returns a properly formatted ICMP request packet with current time as data. This function has to compute the packet's checksum and add it to the header of the outgoing message. 67 | """ 68 | chk_header = struct.pack("bbHHh", icmp_type, icmp_code, 0, req_id, seq_num) 69 | data = struct.pack("d", time.time()) 70 | 71 | calculated_checksum = socket.htons(checksum(chk_header + data)) 72 | 73 | header = struct.pack("bbHHh", icmp_type, icmp_code, 74 | calculated_checksum, req_id, seq_num) 75 | 76 | return header + data 77 | 78 | 79 | def send_request(packet: bytes, addr_dst: str, ttl: int) -> socket: 80 | """ 81 | Send an Echo Request. 82 | Takes packet bytes, destination address, and Time-to-Live value as arguments and returns a new raw socket. This function sets the socket's time-to-live option to the supplied value. 83 | """ 84 | proto = socket.getprotobyname("icmp") 85 | my_icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, proto) 86 | my_icmp_socket.settimeout(TIMEOUT) 87 | my_icmp_socket.setsockopt( 88 | socket.IPPROTO_IP, socket.IP_TTL, struct.pack("I", ttl)) 89 | my_icmp_socket.sendto(packet, (addr_dst, 1)) 90 | return my_icmp_socket 91 | 92 | 93 | def receive_reply(open_socket: socket, timeout: int = 1) -> tuple: 94 | """ 95 | Receive an ICMP reply. 96 | Takes a socket and timeout as arguments and returns a tuple of the received packet and the IP address of the responding host. This function uses select and may raise a TimeoutError is the response does not come soon enough. 97 | """ 98 | 99 | time_left = timeout 100 | started_select = time.time() 101 | 102 | what_ready = select.select([open_socket], [], [], time_left) 103 | 104 | how_long_in_select = time.time() - started_select 105 | time_left = time_left - how_long_in_select 106 | 107 | pkt_rcvd, addr = open_socket.recvfrom(1024) 108 | 109 | if not what_ready[0]: 110 | raise socket.timeout("Request timed out") 111 | if time_left <= 0: 112 | raise socket.timeout("Request timed out") 113 | 114 | return (pkt_rcvd, addr[0]) 115 | 116 | 117 | def parse_reply(packet: bytes) -> bool: 118 | """ 119 | Parse an ICMP reply. 120 | Takes a packet as an argument and returns True if it is a valid (expected) response. This function parses the response header and verifies that the ICMP type is 0, 3, or 11. It also validates the response checksum and raises a ValueError if it's incorrect. 121 | """ 122 | expected_types = [0, 3, 11] 123 | 124 | icmp_data = packet[28:] 125 | icmp_header = packet[20:28] 126 | 127 | pseudo_header = bytearray() 128 | pseudo_header.append(0) 129 | pseudo_header.append(0) 130 | pseudo_header.extend(icmp_header[0:2]) 131 | pseudo_header.extend(icmp_header[4:]) 132 | 133 | icmp_msg_type, icmp_msg_code, check_sum_rcvd, repl_id, sequence = struct.unpack( 134 | "bbHHh", icmp_header) 135 | 136 | if icmp_msg_type not in expected_types: 137 | raise ValueError( 138 | f"Incorrect type: {icmp_msg_type}. Expected {', '.join([str(x) for x in expected_types])}.") 139 | 140 | check_sum_comptd = checksum(pseudo_header + icmp_data) 141 | 142 | if check_sum_rcvd != socket.htons(check_sum_comptd): 143 | raise ValueError(f"Incorrect checksum: {check_sum_rcvd}") 144 | 145 | return True 146 | 147 | 148 | def traceroute(hostname: str) -> None: 149 | """ 150 | Trace the route to a domain. 151 | Takes host (domain) name as an argument and traces a path to that host. The general approach is to have a big loop that sends ICMP Echo Request messages to the host, incrementally increasing TTL value. Each iteration of this loop generates ATTEMPTS (3) messages. There are two possible sources of errors: Timeout (response was not received within timeout) and Value (something is wrong with the response). For each attempts you should do the following: 152 | Format an ICMP Request 153 | Send the request to the destination host 154 | Receive a response (may or may not be a proper ICMP Reply) 155 | Parse the response and check for errors 156 | Print the relevant statistics, if possible 157 | Print the error message, if any 158 | Stop the probe after MAX_HOPS attempts or once a response from the destination host is received 159 | """ 160 | dest_addr = socket.gethostbyname(hostname) 161 | print( 162 | f"Tracing route to {hostname} [{dest_addr}] over a maximum of {MAX_HOPS} hops\n") 163 | 164 | my_id = os.getpid() & 0xFFFF 165 | delim = " " 166 | 167 | for ttl in range(1, MAX_HOPS + 1): 168 | received_success = 0 169 | parsed_success = 0 170 | 171 | print(f"{ttl:<5d}", end="") 172 | 173 | for att in range(ATTEMPTS): 174 | to_error_msg = "" 175 | v_error_msg = "" 176 | 177 | time_sent = time.time() 178 | packet = format_request( 179 | ECHO_REQUEST_TYPE, ECHO_REQUEST_CODE, my_id, att) 180 | my_icmp_socket = send_request(packet, hostname, ttl) 181 | 182 | try: 183 | pkt_rcvd, responder = receive_reply(my_icmp_socket, TIMEOUT) 184 | received_success += 1 185 | # For some reason, excepting TimeoutError alone is not enough, I had to except socket.timeout... 186 | except socket.timeout as te: 187 | # te message is "timed out", and I wanted it to be "Request timed out"! 188 | to_error_msg = "Request " + str(te) 189 | finally: 190 | my_icmp_socket.close() 191 | 192 | if to_error_msg: 193 | print("{:>5s} {:2s}".format("TIME", " "), end="") 194 | continue 195 | 196 | time_rcvd = time.time() 197 | rtt = (time_rcvd - time_sent) * 1000 198 | 199 | try: 200 | parse_reply(pkt_rcvd) 201 | parsed_success += 1 202 | except ValueError as ve: 203 | v_error_msg = str(ve) 204 | 205 | if v_error_msg: 206 | print("{:>5s} {:2s}".format("ERR", " "), end="") 207 | continue 208 | 209 | print(f"{rtt:>5.0f} ms", end="") 210 | 211 | if to_error_msg: 212 | print(f"{delim:3s} {to_error_msg}") 213 | elif v_error_msg: 214 | 215 | print(f"{delim:3s} {v_error_msg}") 216 | else: 217 | print(f"{delim:3s} {responder}") 218 | 219 | if responder == dest_addr: 220 | break 221 | 222 | print("\nTrace complete.") 223 | sys.exit(1) 224 | 225 | 226 | def main(args): 227 | """ 228 | Main Program. 229 | Takes command line arguments and starts the probe. 230 | """ 231 | try: 232 | traceroute(args[1]) 233 | except IndexError: 234 | print(f"Usage: {args[0]} ") 235 | 236 | 237 | if __name__ == "__main__": 238 | """ 239 | Running Main 240 | """ 241 | main(sys.argv) 242 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_afrinic.txt: -------------------------------------------------------------------------------- 1 | Tracing route to afrinic.net [196.216.2.6] over a maximum of 30 hops 2 | 3 | 1 1 ms 1 ms 0 ms 192.168.158.2 4 | 2 1 ms 1 ms 1 ms 192.168.1.1 5 | 3 1 ms 1 ms 1 ms 192.168.0.1 6 | 4 27 ms 27 ms 26 ms 209.181.206.17 7 | 5 27 ms 27 ms 27 ms 209.181.211.129 8 | 6 32 ms 32 ms 35 ms 67.14.8.73 9 | 7 TIME TIME TIME Request timed out 10 | 8 120 ms 119 ms 119 ms 4.69.140.198 11 | 9 123 ms 125 ms 138 ms 195.50.124.34 12 | 10 299 ms 299 ms 300 ms 168.209.100.16 13 | 11 302 ms 728 ms 302 ms 196.26.0.69 14 | 12 297 ms 296 ms 297 ms 196.37.155.180 15 | 13 302 ms 303 ms 303 ms 196.216.3.163 16 | 14 286 ms 286 ms 285 ms 196.216.2.6 17 | 18 | Trace complete. 19 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_apnic.txt: -------------------------------------------------------------------------------- 1 | Tracing route to apnic.net [203.119.101.61] over a maximum of 30 hops 2 | 3 | 1 2 ms 1 ms 1 ms 192.168.158.2 4 | 2 1 ms 1 ms 1 ms 192.168.1.1 5 | 3 2 ms 1 ms 1 ms 192.168.0.1 6 | 4 26 ms 28 ms 26 ms 209.181.206.17 7 | 5 27 ms 26 ms 27 ms 209.181.211.129 8 | 6 72 ms 72 ms 72 ms 67.14.34.202 9 | 7 74 ms 73 ms 75 ms 65.123.13.174 10 | 8 75 ms 75 ms 75 ms 202.84.247.18 11 | 9 220 ms 218 ms 218 ms 202.84.247.38 12 | 10 218 ms 221 ms 221 ms 203.50.13.93 13 | 11 224 ms 224 ms 224 ms 203.50.6.96 14 | 12 238 ms 239 ms 239 ms 203.50.11.179 15 | 13 237 ms 238 ms 238 ms 203.50.51.40 16 | 14 238 ms 237 ms 237 ms 139.130.130.194 17 | 15 238 ms 296 ms 238 ms 203.119.101.61 18 | 19 | Trace complete. 20 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_arin.txt: -------------------------------------------------------------------------------- 1 | Tracing route to arin.net [199.43.0.44] over a maximum of 30 hops 2 | 3 | 1 1 ms 1 ms 0 ms 192.168.158.2 4 | 2 1 ms 1 ms 1 ms 192.168.1.1 5 | 3 2 ms 2 ms 2 ms 192.168.0.1 6 | 4 27 ms 26 ms 26 ms 209.181.206.17 7 | 5 32 ms 26 ms 27 ms 209.181.211.129 8 | 6 46 ms 40 ms 34 ms 67.14.8.73 9 | 7 32 ms 33 ms 33 ms 129.250.8.173 10 | 8 33 ms 36 ms 66 ms 129.250.4.213 11 | 9 53 ms 56 ms 59 ms 129.250.2.138 12 | 10 53 ms 53 ms 52 ms 129.250.2.133 13 | 11 54 ms 55 ms 55 ms 129.250.196.154 14 | 12 53 ms 53 ms 53 ms 199.43.0.130 15 | 13 53 ms 54 ms 53 ms 199.43.0.138 16 | 14 53 ms 53 ms 52 ms 199.43.0.43 17 | 15 53 ms 54 ms 52 ms 199.43.0.43 18 | 16 53 ms 53 ms 53 ms 199.43.0.43 19 | 17 53 ms 52 ms 53 ms 199.43.0.43 20 | 18 52 ms 53 ms 54 ms 199.43.0.43 21 | 19 52 ms 53 ms 52 ms 199.43.0.43 22 | 20 53 ms 52 ms 53 ms 199.43.0.43 23 | 21 52 ms 52 ms 53 ms 199.43.0.43 24 | 22 52 ms 53 ms 52 ms 199.43.0.43 25 | 23 53 ms 53 ms 53 ms 199.43.0.43 26 | 24 52 ms 53 ms 52 ms 199.43.0.43 27 | 25 52 ms 52 ms 53 ms 199.43.0.43 28 | 26 54 ms 53 ms 53 ms 199.43.0.43 29 | 27 52 ms 53 ms 53 ms 199.43.0.43 30 | 28 53 ms 53 ms 53 ms 199.43.0.43 31 | 29 53 ms 52 ms 52 ms 199.43.0.43 32 | 30 52 ms 53 ms 53 ms 199.43.0.43 33 | 34 | Trace complete. 35 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_example.txt: -------------------------------------------------------------------------------- 1 | Tracing route to example.com [93.184.216.34] over a maximum of 30 hops 2 | 3 | 1 1 ms 0 ms 0 ms 192.168.158.2 4 | 2 1 ms 1 ms 1 ms 192.168.1.1 5 | 3 2 ms 2 ms 2 ms 192.168.0.1 6 | 4 26 ms 27 ms 26 ms 209.181.206.17 7 | 5 26 ms 34 ms 33 ms 209.181.211.129 8 | 6 31 ms 31 ms 31 ms 67.14.8.238 9 | 7 34 ms 35 ms 33 ms 192.229.225.192 10 | 8 31 ms 32 ms 31 ms 192.229.225.133 11 | 9 31 ms 32 ms 31 ms 93.184.216.34 12 | 13 | Trace complete. 14 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_example_from_luther.txt: -------------------------------------------------------------------------------- 1 | Tracing route to example.com [93.184.216.34] over a maximum of 30 hops 2 | 3 | 1 1 ms 1 ms 1 ms 172.16.36.2 4 | 2 TIME TIME TIME Request timed out 5 | 3 TIME TIME TIME Request timed out 6 | 4 TIME TIME TIME Request timed out 7 | 5 TIME TIME TIME Request timed out 8 | 6 TIME TIME TIME Request timed out 9 | 7 TIME TIME TIME Request timed out 10 | 8 TIME TIME TIME Request timed out 11 | 9 TIME TIME TIME Request timed out 12 | 10 18 ms 18 ms 17 ms 93.184.216.34 13 | 14 | Trace complete. 15 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_lacnic.txt: -------------------------------------------------------------------------------- 1 | Tracing route to lacnic.net [200.3.14.10] over a maximum of 30 hops 2 | 3 | 1 1 ms 1 ms 0 ms 192.168.158.2 4 | 2 1 ms 1 ms 1 ms 192.168.1.1 5 | 3 2 ms 2 ms 1 ms 192.168.0.1 6 | 4 26 ms 26 ms 26 ms 209.181.206.17 7 | 5 26 ms 26 ms 35 ms 209.181.211.129 8 | 6 32 ms 31 ms 31 ms 67.14.8.73 9 | 7 TIME TIME TIME Request timed out 10 | 8 TIME TIME TIME Request timed out 11 | 9 43 ms 32 ms 32 ms 4.68.62.254 12 | 10 TIME TIME TIME Request timed out 13 | 11 180 ms 169 ms 168 ms 200.186.13.74 14 | 12 168 ms 168 ms 167 ms 200.160.0.157 15 | 13 213 ms 168 ms 167 ms 200.160.0.249 16 | 14 168 ms 167 ms 167 ms 200.160.0.212 17 | 15 167 ms 168 ms 167 ms 200.3.12.34 18 | 16 167 ms 169 ms 167 ms 200.3.14.10 19 | 20 | Trace complete. 21 | -------------------------------------------------------------------------------- /7-Traceroute Tool/traceroute_ripe.txt: -------------------------------------------------------------------------------- 1 | Tracing route to ripe.net [193.0.6.139] over a maximum of 30 hops 2 | 3 | 1 1 ms 0 ms 0 ms 192.168.158.2 4 | 2 1 ms 1 ms 1 ms 192.168.1.1 5 | 3 2 ms 1 ms 1 ms 192.168.0.1 6 | 4 26 ms 27 ms 26 ms 209.181.206.17 7 | 5 27 ms 42 ms 27 ms 209.181.211.129 8 | 6 52 ms 57 ms 51 ms 205.171.134.90 9 | 7 52 ms 51 ms 51 ms 63.146.27.62 10 | 8 138 ms 129 ms 129 ms 195.2.8.90 11 | 9 TIME TIME TIME Request timed out 12 | 10 TIME TIME TIME Request timed out 13 | 11 TIME TIME TIME Request timed out 14 | 12 TIME TIME TIME Request timed out 15 | 13 TIME TIME TIME Request timed out 16 | 14 TIME TIME TIME Request timed out 17 | 15 TIME TIME TIME Request timed out 18 | 16 TIME TIME TIME Request timed out 19 | 17 TIME TIME TIME Request timed out 20 | 18 TIME TIME TIME Request timed out 21 | 19 TIME TIME TIME Request timed out 22 | 20 TIME TIME TIME Request timed out 23 | 21 TIME TIME TIME Request timed out 24 | 22 TIME TIME TIME Request timed out 25 | 23 TIME TIME TIME Request timed out 26 | 24 TIME TIME TIME Request timed out 27 | 25 TIME TIME TIME Request timed out 28 | 26 TIME TIME TIME Request timed out 29 | 27 TIME TIME TIME Request timed out 30 | 28 TIME TIME TIME Request timed out 31 | 29 TIME TIME TIME Request timed out 32 | 30 TIME TIME TIME Request timed out 33 | 34 | Trace complete. 35 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/README.md: -------------------------------------------------------------------------------- 1 | # Trivial Routing Protocol 2 | 3 | ## Task 4 | 5 | In this project you will be writing a set of procedures to implement a distributed asynchronous distance-vector routing protocol. Eventually we'll try to make all the routers work together in the lab environment. In order to achieve general compatibility, it's mandatory that you use **Ubuntu 18.04** as a platform and **Python 3.6** as the implementation language. 6 | 7 | I recommend you implement your router application in stages, from a basic socket application to a full-fledged router. 8 | 9 | This is going to be a challenging project, not only in the sense of correctly implementing the distance-vector routing algorithm but also because your program must handle multiple connections that will operate asynchronously. There are several approaches to correctly deal with a bunch of asynchronous sockets, we are going to use the Python `select` method. The `select` method takes three lists, (sockets I want to **read from**, sockets I want to **write to**, sockets that might have **errors**) and checks all of the sockets lists. When the function returns (either right away, or after a set time), the lists you passed in will have been transformed into lists of sockets that you may want to read, write or check for errors respectively. You can be assured that when you make a read or write call, the call will not block. 10 | 11 | I would strongly suggest that you take the time to write yourself a high-level design for this project before you start writing code. You may also find it useful to write a little server program that keeps multiple connections active and adds messages to a queue. Doing something very simple like this is a good way to learn and check out the problems you are likely to run into with asynchronous communications before you get mired in the whole distance-vector routing. 12 | 13 | Each router should maintain a set of NEIGHBORS (adjacent routers) and a ROUTING_TABLE as a dictionary in the following format: 14 | 15 | ```python 16 | {'destination':[cost, 'next_hop']} 17 | ``` 18 | 19 | ## Stage 1: Read the Configuration File 20 | 21 | We start with a simple application that reads a router's configuration from a text file, displays its status (neighbors and cost of getting to them), and starts listening for incoming UDP connections on port 4300. The configuration contains names of your directly connected neighbors and the cost to reach those neighbors. 22 | 23 | You should write 4 identical files, each one for a different address (127.0.0.**x**) and port (4300**x**). By the end of the project you should be able to test your routers locally, at the very least. 24 | 25 | ### Configuration file format 26 | 27 | ``` 28 | Router_1_IP_address 29 | Neighbor_1_IP_addres Cost_of_getting_to_neighbor_1 30 | Neighbor_2_IP_addres Cost_of_getting_to_neighbor_2 31 | 32 | Router_2_IP_address 33 | Neighbor_1_IP_addres Cost_of_getting_to_neighbor_1 34 | Neighbor_2_IP_addres Cost_of_getting_to_neighbor_2 35 | Neighbor_3_IP_addres Cost_of_getting_to_neighbor_3 36 | ``` 37 | 38 | File *network_1_config.txt* represents the following network: 39 | 40 | ![Simple network](network_1_layout.png) 41 | 42 | ## Stage 1: Welcome to the Party 43 | 44 | Start with a socket application that reads network configuration from a file, binds to port 4300, and prints the routing table. 45 | 46 | ### Stage 1 Functionality 47 | 48 | 1. Read the configuration file 49 | 2. Pick an appropriate address 50 | 3. Display the chosen router's neighborhood (names and costs) 51 | 4. Start listening on **UDP** port 4300 52 | 53 | ## Stage 2: Close Encounters of the Third Kind 54 | 55 | 1. Your program must connect to the IP addresses specified in the configuration file. Your client should accept a path to the configuration file as a command line argument so that we can try out a couple of different configurations. Note that in order to bootstrap the network you are going to need to have your program retry connections that fail. 56 | 57 | 2. Your program must also accept incoming IP connections from neighbors which may inform you of a link cost change, or may ask you to deliver a message to a particular IP address. 58 | 59 | 3. Our protocol will use the following types of messages: 60 | 61 | * **UPDATE (0)** 62 | * **HELLO (1)** 63 | 64 | You should use `bytearray` or `struct` to format and parse messages. 65 | 66 | ### UPDATE message format 67 | 68 | * The first byte of the message (0): 0 69 | 70 | * Next four bytes (1-4): IP address 71 | 72 | * The next byte (5): cost 73 | 74 | * The same pattern (IP address followed by cost) repeats. 75 | 76 | ``` 77 | 0 7 8 15 16 23 24 31 32 39 78 | +--------+--------+--------+--------+--------+ 79 | | Type | IP Address 1 | 80 | +--------+--------+--------+--------+--------+ 81 | | Cost 1 | IP Address 2 | 82 | +--------+--------+--------+--------+--------+ 83 | | Cost 2 | Another record ... 84 | +--------+--------+--------+--------+--------+ 85 | ``` 86 | 87 | ### HELLO message format 88 | 89 | * The first byte of the message (0): 1 90 | 91 | * Next four bytes (1-4): source IP address 92 | 93 | * Next four bytes (5-8): destination IP address 94 | 95 | * The rest of the message (9+): text (characters) 96 | 97 | ``` 98 | 0 7 8 15 16 23 24 31 32 39 99 | +--------+--------+--------+--------+--------+ 100 | | Type | Source IP Address | 101 | +--------+--------+--------+--------+--------+ 102 | | Destination IP Address | Text 103 | +--------+--------+--------+--------+--------+ 104 | | Continuation of message text 105 | +--------+--------+--------+--------+--------+ 106 | ``` 107 | 108 | ### Event loop 109 | 110 | 1. Do we have pending connections? 111 | 112 | 1. Accept new connections 113 | 114 | 2. Add to the listener list 115 | 116 | 3. Add IP addresses to the neighbor list 117 | 118 | 2. Process incoming messages 119 | 120 | 1. If UPDATE, then update the routing table 121 | * Does my vector change? If so, then set flag to `update_vector` 122 | * Print the updated routing table 123 | 124 | 2. If DELIVERY, then forward to the destination 125 | 126 | 3. Is `update_vector` flag set? 127 | 128 | 1. Send the new vector to all neighbors that can accept data 129 | 130 | 4. Check my neighbor list against the list of currently connected neighbors 131 | 132 | 1. If missing neighbors, then try to initiate connections to them 133 | 134 | 2. If successful, then add the new neighbor to list 135 | 136 | 3. Send the new neighbor my distance vector 137 | 138 | ### Stage 2 Functionality 139 | 140 | 1. Read the configuration file name as a command line parameter 141 | 2. Read the neighborhood information from the configuration file 142 | 3. Send a router's table to all neighbors 143 | 4. Receive updates from the neighbors 144 | 5. Keep listening and be ready to update the routing table 145 | 146 | ## Stage 3: Routing 147 | 148 | Write the following routing functions. 149 | 150 | * Read a configuration file for your specific router and add each neighbor to a set of neighbors. 151 | 152 | * Build an initial routing table as a dictionary with nodes as keys. Dictionary values should be a distance to the node and the next hop address. Initially, the dictionary must contain your neighbors only. 153 | 154 | ```python 155 | {'destination':[cost, 'next_hop']} 156 | ``` 157 | 158 | * Format the update message based on the values in the routing table and return the message. For example, a message advertising routes to **127.0.0.1** of cost **10** and to **127.0.0.2** of cost **5** is the following `bytearray`: 159 | 160 | ``` 161 | 0x0 0x7f 0x0 0x0 0x1 0xA 0x7f 0x0 0x0 0x2 0x5 162 | ``` 163 | 164 | * Parse the update message and return `True` if the table has been updated. The function must take a message (raw bytes) and the neighbor's address and update the routing table, if necessary. 165 | 166 | * Print current routing table. The function must print the current routing table in a human-readable format (rows, columns, spacing). 167 | 168 | * Parse a message to deliver. The function must parse the message and extract the destination address. Look up the destination address in the routing table and return the next hop address. 169 | 170 | * Router works with properly implemented routers of other students. 171 | 172 | ## Functions 173 | 174 | ### read_file(filename) 175 | 176 | * Read a configuration file for your specific router and add each neighbor to a set of neighbors. 177 | * Build an initial routing table as a dictionary with nodes as keys. 178 | * Dictionary values should be a distance to the node and the next hop address (ie. {'destination':[cost, 'next_hop']}). 179 | * Initially, the dictionary must contain your neighbors only. 180 | 181 | ### format_update_msg() 182 | 183 | * Format the update message based on the values in the routing table. 184 | * The message advertising routes to 127.0.0.1 of cost 10 and to 127.0.0.2 of cost 5 is a bytearray in the following format 185 | ``` 186 | 0x0 0x7f 0x0 0x0 0x1 0xA 0x7f 0x0 0x0 0x2 0x5 187 | ``` 188 | * The function must return the message. 189 | 190 | ### update_table(msg, neigh_addr) 191 | 192 | * Parse the update message. 193 | * The function must take a message (raw bytes) and the neighbor's address and update the routing table, if necessary. 194 | * The function must return True if the table has been updated. 195 | 196 | ### print_status() 197 | 198 | * Print current routing table. 199 | * The function must print the current routing table in a human-readable format (rows, columns, spacing). 200 | 201 | ### deliver_msg() 202 | 203 | * Parse a message to deliver. 204 | * The function must parse the message and extract the destination address. 205 | * Look up the destination address in the routing table and return the next hop address. 206 | 207 | ### send_update(node) 208 | 209 | * Send updated routing table to the specified node (router) 210 | 211 | ## Running the simulation 212 | 213 | Start each router as follows: 214 | 215 | ``` 216 | python3 router_1.py network_1_config.txt 217 | python3 router_2.py network_1_config.txt 218 | python3 router_3.py network_1_config.txt 219 | python3 router_4.py network_1_config.txt 220 | ``` 221 | 222 | ![Simulation](routing.apng) 223 | 224 | ### Capturing the traffic 225 | 226 | Use the provided dissector *trivial_routing_protocol.lua* to see routing messages in Wireshark. 227 | 228 | ``` 229 | wireshark -X lua_script:trivial_routing_protocol.lua routing_capture.pcapng & 230 | ``` 231 | 232 | ## Grading 233 | 234 | Functionality | Points 235 | ---|--- 236 | Read network configuration file | 20 237 | Display router's Distance Vector | 20 238 | Format UPDATE message | 20 239 | Send UPDATE message to all neighbors, if necessary | 20 240 | Receive UPDATE message from all neighbors | 20 241 | Parse UPDATE message | 20 242 | Update Distance Vector, if necessary | 20 243 | Ignore incoming UPDATE message, if possible | 20 244 | Format and send HELLO message | 20 245 | Parse and forward/display HELLO message | 20 246 | **Total** | **200** 247 | 248 | ## References 249 | 250 | * [socket — Low-level networking interface — Python 3.7.1 documentation](https://docs.python.org/3/library/socket.html) 251 | 252 | * [select — Waiting for I/O completion — Python 3.7.1 documentation](https://docs.python.org/3/library/select.html) 253 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/8-Distance Vector Routing Protocol/__init__.py -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/network_1_config.toml: -------------------------------------------------------------------------------- 1 | # This is a simple network configuration file 2 | 3 | [[routers]] 4 | address = "127.0.0.1" 5 | neighbors = [ 6 | 7 | { address = "127.0.0.2", cost = 1 }, 8 | { address = "127.0.0.3", cost = 3 }, 9 | { address = "127.0.0.4", cost = 7 } 10 | ] 11 | 12 | [[routers]] 13 | address = "127.0.0.2" 14 | neighbors = [ 15 | { address = "127.0.0.1", cost = 1 }, 16 | { address = "127.0.0.3", cost = 1 } 17 | ] 18 | 19 | [[routers]] 20 | address = "127.0.0.3" 21 | neighbors = [ 22 | { address = "127.0.0.1", cost = 3 }, 23 | { address = "127.0.0.2", cost = 1 }, 24 | { address = "127.0.0.4", cost = 2 } 25 | ] 26 | 27 | [[routers]] 28 | address = "127.0.0.4" 29 | neighbors = [ 30 | { address = "127.0.0.1", cost = 7 }, 31 | { address = "127.0.0.3", cost = 2 } 32 | ] 33 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/network_1_config.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | 127.0.0.2 1 3 | 127.0.0.3 3 4 | 127.0.0.4 7 5 | 6 | 127.0.0.2 7 | 127.0.0.1 1 8 | 127.0.0.3 1 9 | 10 | 127.0.0.3 11 | 127.0.0.1 3 12 | 127.0.0.2 1 13 | 127.0.0.4 2 14 | 15 | 127.0.0.4 16 | 127.0.0.1 7 17 | 127.0.0.3 2 18 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/network_1_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/8-Distance Vector Routing Protocol/network_1_layout.png -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/network_2_config.toml: -------------------------------------------------------------------------------- 1 | # This is a network 2 configuration file 2 | 3 | [[routers]] 4 | name = "t" 5 | address = "127.0.0.1" 6 | neighbors = [ 7 | 8 | { address = "127.0.0.2", cost = 2 }, 9 | { address = "127.0.0.3", cost = 4 }, 10 | { address = "127.0.0.6", cost = 7 } 11 | ] 12 | 13 | [[routers]] 14 | name = "u" 15 | address = "127.0.0.2" 16 | neighbors = [ 17 | { address = "127.0.0.1", cost = 2 }, 18 | { address = "127.0.0.3", cost = 3 }, 19 | { address = "127.0.0.4", cost = 3 } 20 | ] 21 | 22 | [[routers]] 23 | name = "v" 24 | address = "127.0.0.3" 25 | neighbors = [ 26 | { address = "127.0.0.1", cost = 4 }, 27 | { address = "127.0.0.2", cost = 3 }, 28 | { address = "127.0.0.4", cost = 4 }, 29 | { address = "127.0.0.5", cost = 3 }, 30 | { address = "127.0.0.6", cost = 8 } 31 | ] 32 | 33 | [[routers]] 34 | name = "w" 35 | address = "127.0.0.4" 36 | neighbors = [ 37 | { address = "127.0.0.2", cost = 3 }, 38 | { address = "127.0.0.3", cost = 4 }, 39 | { address = "127.0.0.5", cost = 6 } 40 | ] 41 | 42 | [[routers]] 43 | name = "x" 44 | address = "127.0.0.5" 45 | neighbors = [ 46 | { address = "127.0.0.3", cost = 3 }, 47 | { address = "127.0.0.4", cost = 6 }, 48 | { address = "127.0.0.6", cost = 6 }, 49 | { address = "127.0.0.7", cost = 8 } 50 | ] 51 | 52 | [[routers]] 53 | name = "y" 54 | address = "127.0.0.6" 55 | neighbors = [ 56 | { address = "127.0.0.1", cost = 7 }, 57 | { address = "127.0.0.3", cost = 8 }, 58 | { address = "127.0.0.5", cost = 6 }, 59 | { address = "127.0.0.7", cost = 12 } 60 | ] 61 | 62 | [[routers]] 63 | name = "z" 64 | address = "127.0.0.7" 65 | neighbors = [ 66 | { address = "127.0.0.5", cost = 8 }, 67 | { address = "127.0.0.6", cost = 12 } 68 | ] 69 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/network_2_config.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | 127.0.0.2 2 3 | 127.0.0.3 4 4 | 127.0.0.6 7 5 | 6 | 127.0.0.2 7 | 127.0.0.1 2 8 | 127.0.0.3 3 9 | 127.0.0.4 3 10 | 11 | 127.0.0.3 12 | 127.0.0.1 4 13 | 127.0.0.2 3 14 | 127.0.0.4 4 15 | 127.0.0.5 3 16 | 127.0.0.6 8 17 | 18 | 127.0.0.4 19 | 127.0.0.2 3 20 | 127.0.0.3 4 21 | 127.0.0.5 6 22 | 23 | 127.0.0.5 24 | 127.0.0.3 3 25 | 127.0.0.4 6 26 | 127.0.0.6 6 27 | 127.0.0.7 8 28 | 29 | 127.0.0.6 30 | 127.0.0.1 7 31 | 127.0.0.3 8 32 | 127.0.0.5 6 33 | 127.0.0.7 12 34 | 35 | 127.0.0.7 36 | 127.0.0.5 8 37 | 127.0.0.6 12 38 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/network_2_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/8-Distance Vector Routing Protocol/network_2_layout.png -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/router_1.py: -------------------------------------------------------------------------------- 1 | """Router implementation using UDP sockets""" 2 | #!/usr/bin/env python3 3 | # encoding: UTF-8 4 | 5 | 6 | import os 7 | import random 8 | import select 9 | from socket import socket, AF_INET, SOCK_DGRAM 10 | import struct 11 | import sys 12 | import time 13 | 14 | HOST_ID = os.path.splitext(__file__)[0].split("_")[-1] 15 | THIS_NODE = f"127.0.0.{HOST_ID}" 16 | PORT = 4300 17 | NEIGHBORS = set() 18 | ROUTING_TABLE = {} 19 | TIMEOUT = 5 20 | MESSAGES = [ 21 | "Cosmic Cuttlefish", 22 | "Bionic Beaver", 23 | "Xenial Xerus", 24 | "Trusty Tahr", 25 | "Precise Pangolin" 26 | ] 27 | 28 | CONNECTED = set() 29 | NEW_CONNECTIONS = set() 30 | 31 | # MINE = {} # TODO 32 | # NEIGHBORS_EXTENDED = {} # TODO 33 | 34 | INPUTS = [] 35 | 36 | 37 | def read_file(filename: str) -> None: 38 | """Read config file""" 39 | 40 | # Can be as simple as 41 | with open(filename, "r") as f: 42 | lines = f.readlines() 43 | found = False 44 | for line in lines: 45 | line = line.split() 46 | if len(line) == 1: 47 | if line[0] == THIS_NODE: 48 | found = True 49 | elif found == True: 50 | found = False 51 | elif len(line) == 2 and found: 52 | if found: 53 | ROUTING_TABLE.update({line[0]: [int(line[1]), line[0]]}) 54 | NEIGHBORS.add(line[0]) 55 | 56 | # This is way more complicated - meant for Dijkstra's comparison with Distance Vector # TODO 57 | """ skip = False 58 | current_neighbor = "" 59 | 60 | # This is more complicated than it should be - but I was debugging and some of the stuff here is used later for doulbe checking results with Dijkstras 61 | with open(filename, "r") as f: 62 | for line in f: 63 | line = line.split() 64 | 65 | if len(line) == 0: 66 | skip = False 67 | continue 68 | elif len(line) == 1: 69 | if line[0] == THIS_NODE: 70 | skip = True 71 | continue 72 | current_neighbor = line[0] 73 | elif len(line) == 2: 74 | if skip: 75 | NEIGHBORS.add(line[0]) 76 | MINE[line[0]] = line[1] 77 | ROUTING_TABLE[line[0]] = [line[1], line[0]] 78 | continue 79 | if not current_neighbor in NEIGHBORS_EXTENDED: 80 | NEIGHBORS_EXTENDED[current_neighbor] = [] 81 | NEIGHBORS_EXTENDED[current_neighbor].append( 82 | [line[0], line[1]]) 83 | 84 | # print(MINE) 85 | # print(NEIGHBORS) 86 | # print(NEIGHBORS_EXTENDED) 87 | 88 | # print(THIS_NODE) 89 | 90 | # print(ROUTING_TABLE) 91 | 92 | # Simulating Distance Vector Algorithm with Dijkstras Algorithm 93 | 94 | for neighbor in NEIGHBORS: 95 | neighbor_cost = int(MINE[neighbor]) 96 | # print("\t Neighbor:", neighbor, "cost:", neighbor_cost) 97 | for node in NEIGHBORS_EXTENDED[neighbor]: 98 | if THIS_NODE == node[0]: 99 | continue 100 | 101 | hop_cost = int(node[1]) + neighbor_cost 102 | 103 | if (not node[0] in ROUTING_TABLE) or (int(ROUTING_TABLE[node[0]][0]) > hop_cost): 104 | ROUTING_TABLE[node[0]] = [hop_cost, neighbor] 105 | 106 | # print("\t\t ", node[0], "cost", hop_cost) 107 | 108 | # break 109 | # print(NEIGHBORS_EXTENDED[neighbor]) 110 | 111 | # print(ROUTING_TABLE) """ 112 | 113 | 114 | def format_update(): 115 | """Format update message""" 116 | 117 | msg = bytearray() 118 | msg_type = 0 119 | msg.extend(msg_type.to_bytes(1, byteorder='big')) 120 | 121 | for dest, cost in ROUTING_TABLE.items(): 122 | a, b, c, d = tuple(map(lambda x: int(x), dest.split("."))) 123 | cost = int(cost[0]) 124 | msg_bytes = struct.pack("bbbbb", a, b, c, d, cost) 125 | msg.extend(msg_bytes) 126 | 127 | return msg 128 | 129 | 130 | def parse_update(msg, neigh_addr): 131 | """Update routing table""" 132 | 133 | msg = msg[1:] 134 | update = False 135 | 136 | for val in range(0, len(msg), 5): 137 | if len(msg) < 5: 138 | break 139 | temp = msg[val:val+5] 140 | 141 | values = struct.unpack('BBBBB', temp) 142 | values = list(map(lambda x: str(x), values)) 143 | ip = ".".join(values[:4]) 144 | cost = int(values[4]) 145 | 146 | if ip in ROUTING_TABLE: 147 | if ROUTING_TABLE[ip][0] > int(cost + ROUTING_TABLE[neigh_addr][0]): 148 | ROUTING_TABLE[ip] = [ 149 | (cost + int(ROUTING_TABLE[neigh_addr][0])), ROUTING_TABLE[neigh_addr][1]] 150 | update = True 151 | elif ip == THIS_NODE: 152 | continue 153 | else: 154 | ROUTING_TABLE[ip] = [ 155 | (cost + int(ROUTING_TABLE[neigh_addr][0])), neigh_addr] 156 | update = True 157 | 158 | return update 159 | 160 | 161 | def send_update(node): 162 | """Send update""" 163 | sock = socket(AF_INET, SOCK_DGRAM) 164 | sock.bind((THIS_NODE, PORT)) 165 | msg = format_update() 166 | 167 | port_id = node.split(".")[-1] 168 | table = format_update() 169 | 170 | sock.sendto(msg, (node, PORT+int(port_id))) 171 | sock.close() 172 | 173 | 174 | def format_hello(msg_txt, src_node, dst_node): 175 | """Format hello message""" 176 | msg_type = 1 177 | 178 | a, b, c, d = tuple(map(lambda x: int(x), src_node.split("."))) 179 | e, f, g, h = tuple(map(lambda x: int(x), dst_node.split("."))) 180 | 181 | rest = bytes(msg_txt, "utf-8") 182 | msg = struct.pack("bbbbbbbbb{}s".format( 183 | len(rest)), msg_type, a, b, c, d, e, f, g, h, rest) 184 | 185 | return msg 186 | 187 | 188 | def parse_hello(msg): 189 | """Send the message to an appropriate next hop""" 190 | msg_type, a, b, c, d, e, f, g, h, msg_txt = struct.unpack( 191 | 'bbbbbbbbb{}s'.format(len(msg[9:])), msg) 192 | 193 | ip = list(map(lambda x: str(x), [a, b, c, d])) 194 | src_addr = ".".join(ip) 195 | 196 | ip = list(map(lambda x: str(x), [e, f, g, h])) 197 | dst_addr = ".".join(ip) 198 | 199 | if dst_addr == THIS_NODE: 200 | print(time.strftime('%H:%M:%S'), 201 | f'| Received {msg_txt.decode()} from {src_addr}') 202 | else: 203 | print(time.strftime('%H:%M:%S'), 204 | f'| Forwarding {msg_txt.decode()} from {src_addr} to {dst_addr} via {ROUTING_TABLE[dst_addr][1]}') 205 | send_hello(msg_txt.decode(), src_addr, dst_addr) 206 | 207 | 208 | def send_hello(msg_txt, src_node, dst_node): 209 | """Send a message""" 210 | sock = socket(AF_INET, SOCK_DGRAM) 211 | sock.bind((THIS_NODE, PORT)) 212 | 213 | msg = format_hello(msg_txt, src_node, dst_node) 214 | 215 | port_id = ROUTING_TABLE[dst_node][1].split(".")[-1] 216 | 217 | sock.sendto(msg, (ROUTING_TABLE[dst_node][1], PORT+int(port_id))) 218 | sock.close() 219 | 220 | 221 | def print_status(): 222 | """Print status""" 223 | 224 | print('{:^20}{:^10}{:^20}'.format('Host', "Cost", "Via")) 225 | for node in ROUTING_TABLE: 226 | print('{:^20}{:^10}{:^20}'.format( 227 | node, ROUTING_TABLE[node][0], ROUTING_TABLE[node][1])) 228 | 229 | 230 | def main(args: list): 231 | """Router main loop""" 232 | 233 | print(time.strftime("%H:%M:%S"), "| Router", THIS_NODE, "here") 234 | 235 | read_file(args[1]) 236 | 237 | with socket(AF_INET, SOCK_DGRAM) as server: 238 | print(time.strftime('%H:%M:%S'), 239 | f'| Binding to {THIS_NODE}:{PORT+int(HOST_ID)}') 240 | server.bind((THIS_NODE, PORT+int(HOST_ID))) 241 | 242 | INPUTS = [server] 243 | 244 | print(time.strftime('%H:%M:%S'), 245 | f'| Listening on {THIS_NODE}:{PORT+int(HOST_ID)}') 246 | 247 | time.sleep(random.randint(2, 8)) 248 | 249 | for n in NEIGHBORS: 250 | send_update(n) 251 | 252 | print_status() 253 | 254 | while INPUTS: 255 | readable, writable, exceptional = select.select( 256 | INPUTS, [], [], TIMEOUT) 257 | update_vector = False 258 | 259 | if random.randint(0, 1000000) < 10: 260 | time.sleep(random.randint(2, 8)) 261 | send_msg = random.choice(MESSAGES) 262 | send_to = random.choice(list(ROUTING_TABLE.keys())) 263 | send_hello(send_msg, THIS_NODE, send_to) 264 | print(time.strftime('%H:%M:%S'), 265 | f'| Sending {send_msg} to {send_to} via {ROUTING_TABLE[send_to][1]}') 266 | 267 | for r in readable: 268 | 269 | recv, addr = server.recvfrom(1024) 270 | if recv: 271 | msg_type = int.from_bytes(recv[0:1], byteorder='big') 272 | if msg_type == 0: 273 | update_vector = parse_update(recv, addr[0]) 274 | if update_vector: 275 | time.sleep(random.randint(2, 8)) 276 | print(time.strftime('%H:%M:%S'), 277 | f'| Table updated with information from {addr[0]}') 278 | print_status() 279 | elif msg_type == 1: 280 | parse_hello(recv) 281 | else: 282 | print(time.strftime('%H:%M:%S'), 283 | f'| Received unexpected message from {addr}. Message will be ignored.') 284 | 285 | if update_vector: 286 | for n in NEIGHBORS: 287 | send_update(n) 288 | 289 | for i in INPUTS: 290 | CONNECTED.add(i.getsockname()[0]) 291 | 292 | NEW_CONNECTIONS = NEIGHBORS - CONNECTED 293 | 294 | for n in NEW_CONNECTIONS: 295 | send_update(n) 296 | 297 | 298 | if __name__ == "__main__": 299 | main(sys.argv) 300 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/router_2.py: -------------------------------------------------------------------------------- 1 | """Router implementation using UDP sockets""" 2 | #!/usr/bin/env python3 3 | # encoding: UTF-8 4 | 5 | 6 | import os 7 | import random 8 | import select 9 | from socket import socket, AF_INET, SOCK_DGRAM 10 | import struct 11 | import sys 12 | import time 13 | 14 | HOST_ID = os.path.splitext(__file__)[0].split("_")[-1] 15 | THIS_NODE = f"127.0.0.{HOST_ID}" 16 | PORT = 4300 17 | NEIGHBORS = set() 18 | ROUTING_TABLE = {} 19 | TIMEOUT = 5 20 | MESSAGES = [ 21 | "Cosmic Cuttlefish", 22 | "Bionic Beaver", 23 | "Xenial Xerus", 24 | "Trusty Tahr", 25 | "Precise Pangolin" 26 | ] 27 | 28 | CONNECTED = set() 29 | NEW_CONNECTIONS = set() 30 | 31 | # MINE = {} # TODO 32 | # NEIGHBORS_EXTENDED = {} # TODO 33 | 34 | INPUTS = [] 35 | 36 | 37 | def read_file(filename: str) -> None: 38 | """Read config file""" 39 | 40 | # Can be as simple as 41 | with open(filename, "r") as f: 42 | lines = f.readlines() 43 | found = False 44 | for line in lines: 45 | line = line.split() 46 | if len(line) == 1: 47 | if line[0] == THIS_NODE: 48 | found = True 49 | elif found == True: 50 | found = False 51 | elif len(line) == 2 and found: 52 | if found: 53 | ROUTING_TABLE.update({line[0]: [int(line[1]), line[0]]}) 54 | NEIGHBORS.add(line[0]) 55 | 56 | # This is way more complicated - meant for Dijkstra's comparison with Distance Vector # TODO 57 | """ skip = False 58 | current_neighbor = "" 59 | 60 | # This is more complicated than it should be - but I was debugging and some of the stuff here is used later for doulbe checking results with Dijkstras 61 | with open(filename, "r") as f: 62 | for line in f: 63 | line = line.split() 64 | 65 | if len(line) == 0: 66 | skip = False 67 | continue 68 | elif len(line) == 1: 69 | if line[0] == THIS_NODE: 70 | skip = True 71 | continue 72 | current_neighbor = line[0] 73 | elif len(line) == 2: 74 | if skip: 75 | NEIGHBORS.add(line[0]) 76 | MINE[line[0]] = line[1] 77 | ROUTING_TABLE[line[0]] = [line[1], line[0]] 78 | continue 79 | if not current_neighbor in NEIGHBORS_EXTENDED: 80 | NEIGHBORS_EXTENDED[current_neighbor] = [] 81 | NEIGHBORS_EXTENDED[current_neighbor].append( 82 | [line[0], line[1]]) 83 | 84 | # print(MINE) 85 | # print(NEIGHBORS) 86 | # print(NEIGHBORS_EXTENDED) 87 | 88 | # print(THIS_NODE) 89 | 90 | # print(ROUTING_TABLE) 91 | 92 | # Simulating Distance Vector Algorithm with Dijkstras Algorithm 93 | 94 | for neighbor in NEIGHBORS: 95 | neighbor_cost = int(MINE[neighbor]) 96 | # print("\t Neighbor:", neighbor, "cost:", neighbor_cost) 97 | for node in NEIGHBORS_EXTENDED[neighbor]: 98 | if THIS_NODE == node[0]: 99 | continue 100 | 101 | hop_cost = int(node[1]) + neighbor_cost 102 | 103 | if (not node[0] in ROUTING_TABLE) or (int(ROUTING_TABLE[node[0]][0]) > hop_cost): 104 | ROUTING_TABLE[node[0]] = [hop_cost, neighbor] 105 | 106 | # print("\t\t ", node[0], "cost", hop_cost) 107 | 108 | # break 109 | # print(NEIGHBORS_EXTENDED[neighbor]) 110 | 111 | # print(ROUTING_TABLE) """ 112 | 113 | 114 | def format_update(): 115 | """Format update message""" 116 | 117 | msg = bytearray() 118 | msg_type = 0 119 | msg.extend(msg_type.to_bytes(1, byteorder='big')) 120 | 121 | for dest, cost in ROUTING_TABLE.items(): 122 | a, b, c, d = tuple(map(lambda x: int(x), dest.split("."))) 123 | cost = int(cost[0]) 124 | msg_bytes = struct.pack("bbbbb", a, b, c, d, cost) 125 | msg.extend(msg_bytes) 126 | 127 | return msg 128 | 129 | 130 | def parse_update(msg, neigh_addr): 131 | """Update routing table""" 132 | 133 | msg = msg[1:] 134 | update = False 135 | 136 | for val in range(0, len(msg), 5): 137 | if len(msg) < 5: 138 | break 139 | temp = msg[val:val+5] 140 | 141 | values = struct.unpack('BBBBB', temp) 142 | values = list(map(lambda x: str(x), values)) 143 | ip = ".".join(values[:4]) 144 | cost = int(values[4]) 145 | 146 | if ip in ROUTING_TABLE: 147 | if ROUTING_TABLE[ip][0] > int(cost + ROUTING_TABLE[neigh_addr][0]): 148 | ROUTING_TABLE[ip] = [ 149 | (cost + int(ROUTING_TABLE[neigh_addr][0])), ROUTING_TABLE[neigh_addr][1]] 150 | update = True 151 | elif ip == THIS_NODE: 152 | continue 153 | else: 154 | ROUTING_TABLE[ip] = [ 155 | (cost + int(ROUTING_TABLE[neigh_addr][0])), neigh_addr] 156 | update = True 157 | 158 | return update 159 | 160 | 161 | def send_update(node): 162 | """Send update""" 163 | sock = socket(AF_INET, SOCK_DGRAM) 164 | sock.bind((THIS_NODE, PORT)) 165 | msg = format_update() 166 | 167 | port_id = node.split(".")[-1] 168 | table = format_update() 169 | 170 | sock.sendto(msg, (node, PORT+int(port_id))) 171 | sock.close() 172 | 173 | 174 | def format_hello(msg_txt, src_node, dst_node): 175 | """Format hello message""" 176 | msg_type = 1 177 | 178 | a, b, c, d = tuple(map(lambda x: int(x), src_node.split("."))) 179 | e, f, g, h = tuple(map(lambda x: int(x), dst_node.split("."))) 180 | 181 | rest = bytes(msg_txt, "utf-8") 182 | msg = struct.pack("bbbbbbbbb{}s".format( 183 | len(rest)), msg_type, a, b, c, d, e, f, g, h, rest) 184 | 185 | return msg 186 | 187 | 188 | def parse_hello(msg): 189 | """Send the message to an appropriate next hop""" 190 | msg_type, a, b, c, d, e, f, g, h, msg_txt = struct.unpack( 191 | 'bbbbbbbbb{}s'.format(len(msg[9:])), msg) 192 | 193 | ip = list(map(lambda x: str(x), [a, b, c, d])) 194 | src_addr = ".".join(ip) 195 | 196 | ip = list(map(lambda x: str(x), [e, f, g, h])) 197 | dst_addr = ".".join(ip) 198 | 199 | if dst_addr == THIS_NODE: 200 | print(time.strftime('%H:%M:%S'), 201 | f'| Received {msg_txt.decode()} from {src_addr}') 202 | else: 203 | print(time.strftime('%H:%M:%S'), 204 | f'| Forwarding {msg_txt.decode()} from {src_addr} to {dst_addr} via {ROUTING_TABLE[dst_addr][1]}') 205 | send_hello(msg_txt.decode(), src_addr, dst_addr) 206 | 207 | 208 | def send_hello(msg_txt, src_node, dst_node): 209 | """Send a message""" 210 | sock = socket(AF_INET, SOCK_DGRAM) 211 | sock.bind((THIS_NODE, PORT)) 212 | 213 | msg = format_hello(msg_txt, src_node, dst_node) 214 | 215 | port_id = ROUTING_TABLE[dst_node][1].split(".")[-1] 216 | 217 | sock.sendto(msg, (ROUTING_TABLE[dst_node][1], PORT+int(port_id))) 218 | sock.close() 219 | 220 | 221 | def print_status(): 222 | """Print status""" 223 | 224 | print('{:^20}{:^10}{:^20}'.format('Host', "Cost", "Via")) 225 | for node in ROUTING_TABLE: 226 | print('{:^20}{:^10}{:^20}'.format( 227 | node, ROUTING_TABLE[node][0], ROUTING_TABLE[node][1])) 228 | 229 | 230 | def main(args: list): 231 | """Router main loop""" 232 | 233 | print(time.strftime("%H:%M:%S"), "| Router", THIS_NODE, "here") 234 | 235 | read_file(args[1]) 236 | 237 | with socket(AF_INET, SOCK_DGRAM) as server: 238 | print(time.strftime('%H:%M:%S'), 239 | f'| Binding to {THIS_NODE}:{PORT+int(HOST_ID)}') 240 | server.bind((THIS_NODE, PORT+int(HOST_ID))) 241 | 242 | INPUTS = [server] 243 | 244 | print(time.strftime('%H:%M:%S'), 245 | f'| Listening on {THIS_NODE}:{PORT+int(HOST_ID)}') 246 | 247 | time.sleep(random.randint(2, 8)) 248 | 249 | for n in NEIGHBORS: 250 | send_update(n) 251 | 252 | print_status() 253 | 254 | while INPUTS: 255 | readable, writable, exceptional = select.select( 256 | INPUTS, [], [], TIMEOUT) 257 | update_vector = False 258 | 259 | if random.randint(0, 1000000) < 10: 260 | time.sleep(random.randint(2, 8)) 261 | send_msg = random.choice(MESSAGES) 262 | send_to = random.choice(list(ROUTING_TABLE.keys())) 263 | send_hello(send_msg, THIS_NODE, send_to) 264 | print(time.strftime('%H:%M:%S'), 265 | f'| Sending {send_msg} to {send_to} via {ROUTING_TABLE[send_to][1]}') 266 | 267 | for r in readable: 268 | 269 | recv, addr = server.recvfrom(1024) 270 | if recv: 271 | msg_type = int.from_bytes(recv[0:1], byteorder='big') 272 | if msg_type == 0: 273 | update_vector = parse_update(recv, addr[0]) 274 | if update_vector: 275 | time.sleep(random.randint(2, 8)) 276 | print(time.strftime('%H:%M:%S'), 277 | f'| Table updated with information from {addr[0]}') 278 | print_status() 279 | elif msg_type == 1: 280 | parse_hello(recv) 281 | else: 282 | print(time.strftime('%H:%M:%S'), 283 | f'| Received unexpected message from {addr}. Message will be ignored.') 284 | 285 | if update_vector: 286 | for n in NEIGHBORS: 287 | send_update(n) 288 | 289 | for i in INPUTS: 290 | CONNECTED.add(i.getsockname()[0]) 291 | 292 | NEW_CONNECTIONS = NEIGHBORS - CONNECTED 293 | 294 | for n in NEW_CONNECTIONS: 295 | send_update(n) 296 | 297 | 298 | if __name__ == "__main__": 299 | main(sys.argv) 300 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/router_3.py: -------------------------------------------------------------------------------- 1 | """Router implementation using UDP sockets""" 2 | #!/usr/bin/env python3 3 | # encoding: UTF-8 4 | 5 | 6 | import os 7 | import random 8 | import select 9 | from socket import socket, AF_INET, SOCK_DGRAM 10 | import struct 11 | import sys 12 | import time 13 | 14 | HOST_ID = os.path.splitext(__file__)[0].split("_")[-1] 15 | THIS_NODE = f"127.0.0.{HOST_ID}" 16 | PORT = 4300 17 | NEIGHBORS = set() 18 | ROUTING_TABLE = {} 19 | TIMEOUT = 5 20 | MESSAGES = [ 21 | "Cosmic Cuttlefish", 22 | "Bionic Beaver", 23 | "Xenial Xerus", 24 | "Trusty Tahr", 25 | "Precise Pangolin" 26 | ] 27 | 28 | CONNECTED = set() 29 | NEW_CONNECTIONS = set() 30 | 31 | # MINE = {} # TODO 32 | # NEIGHBORS_EXTENDED = {} # TODO 33 | 34 | INPUTS = [] 35 | 36 | 37 | def read_file(filename: str) -> None: 38 | """Read config file""" 39 | 40 | # Can be as simple as 41 | with open(filename, "r") as f: 42 | lines = f.readlines() 43 | found = False 44 | for line in lines: 45 | line = line.split() 46 | if len(line) == 1: 47 | if line[0] == THIS_NODE: 48 | found = True 49 | elif found == True: 50 | found = False 51 | elif len(line) == 2 and found: 52 | if found: 53 | ROUTING_TABLE.update({line[0]: [int(line[1]), line[0]]}) 54 | NEIGHBORS.add(line[0]) 55 | 56 | # This is way more complicated - meant for Dijkstra's comparison with Distance Vector # TODO 57 | """ skip = False 58 | current_neighbor = "" 59 | 60 | # This is more complicated than it should be - but I was debugging and some of the stuff here is used later for doulbe checking results with Dijkstras 61 | with open(filename, "r") as f: 62 | for line in f: 63 | line = line.split() 64 | 65 | if len(line) == 0: 66 | skip = False 67 | continue 68 | elif len(line) == 1: 69 | if line[0] == THIS_NODE: 70 | skip = True 71 | continue 72 | current_neighbor = line[0] 73 | elif len(line) == 2: 74 | if skip: 75 | NEIGHBORS.add(line[0]) 76 | MINE[line[0]] = line[1] 77 | ROUTING_TABLE[line[0]] = [line[1], line[0]] 78 | continue 79 | if not current_neighbor in NEIGHBORS_EXTENDED: 80 | NEIGHBORS_EXTENDED[current_neighbor] = [] 81 | NEIGHBORS_EXTENDED[current_neighbor].append( 82 | [line[0], line[1]]) 83 | 84 | # print(MINE) 85 | # print(NEIGHBORS) 86 | # print(NEIGHBORS_EXTENDED) 87 | 88 | # print(THIS_NODE) 89 | 90 | # print(ROUTING_TABLE) 91 | 92 | # Simulating Distance Vector Algorithm with Dijkstras Algorithm 93 | 94 | for neighbor in NEIGHBORS: 95 | neighbor_cost = int(MINE[neighbor]) 96 | # print("\t Neighbor:", neighbor, "cost:", neighbor_cost) 97 | for node in NEIGHBORS_EXTENDED[neighbor]: 98 | if THIS_NODE == node[0]: 99 | continue 100 | 101 | hop_cost = int(node[1]) + neighbor_cost 102 | 103 | if (not node[0] in ROUTING_TABLE) or (int(ROUTING_TABLE[node[0]][0]) > hop_cost): 104 | ROUTING_TABLE[node[0]] = [hop_cost, neighbor] 105 | 106 | # print("\t\t ", node[0], "cost", hop_cost) 107 | 108 | # break 109 | # print(NEIGHBORS_EXTENDED[neighbor]) 110 | 111 | # print(ROUTING_TABLE) """ 112 | 113 | 114 | def format_update(): 115 | """Format update message""" 116 | 117 | msg = bytearray() 118 | msg_type = 0 119 | msg.extend(msg_type.to_bytes(1, byteorder='big')) 120 | 121 | for dest, cost in ROUTING_TABLE.items(): 122 | a, b, c, d = tuple(map(lambda x: int(x), dest.split("."))) 123 | cost = int(cost[0]) 124 | msg_bytes = struct.pack("bbbbb", a, b, c, d, cost) 125 | msg.extend(msg_bytes) 126 | 127 | return msg 128 | 129 | 130 | def parse_update(msg, neigh_addr): 131 | """Update routing table""" 132 | 133 | msg = msg[1:] 134 | update = False 135 | 136 | for val in range(0, len(msg), 5): 137 | if len(msg) < 5: 138 | break 139 | temp = msg[val:val+5] 140 | 141 | values = struct.unpack('BBBBB', temp) 142 | values = list(map(lambda x: str(x), values)) 143 | ip = ".".join(values[:4]) 144 | cost = int(values[4]) 145 | 146 | if ip in ROUTING_TABLE: 147 | if ROUTING_TABLE[ip][0] > int(cost + ROUTING_TABLE[neigh_addr][0]): 148 | ROUTING_TABLE[ip] = [ 149 | (cost + int(ROUTING_TABLE[neigh_addr][0])), ROUTING_TABLE[neigh_addr][1]] 150 | update = True 151 | elif ip == THIS_NODE: 152 | continue 153 | else: 154 | ROUTING_TABLE[ip] = [ 155 | (cost + int(ROUTING_TABLE[neigh_addr][0])), neigh_addr] 156 | update = True 157 | 158 | return update 159 | 160 | 161 | def send_update(node): 162 | """Send update""" 163 | sock = socket(AF_INET, SOCK_DGRAM) 164 | sock.bind((THIS_NODE, PORT)) 165 | msg = format_update() 166 | 167 | port_id = node.split(".")[-1] 168 | table = format_update() 169 | 170 | sock.sendto(msg, (node, PORT+int(port_id))) 171 | sock.close() 172 | 173 | 174 | def format_hello(msg_txt, src_node, dst_node): 175 | """Format hello message""" 176 | msg_type = 1 177 | 178 | a, b, c, d = tuple(map(lambda x: int(x), src_node.split("."))) 179 | e, f, g, h = tuple(map(lambda x: int(x), dst_node.split("."))) 180 | 181 | rest = bytes(msg_txt, "utf-8") 182 | msg = struct.pack("bbbbbbbbb{}s".format( 183 | len(rest)), msg_type, a, b, c, d, e, f, g, h, rest) 184 | 185 | return msg 186 | 187 | 188 | def parse_hello(msg): 189 | """Send the message to an appropriate next hop""" 190 | msg_type, a, b, c, d, e, f, g, h, msg_txt = struct.unpack( 191 | 'bbbbbbbbb{}s'.format(len(msg[9:])), msg) 192 | 193 | ip = list(map(lambda x: str(x), [a, b, c, d])) 194 | src_addr = ".".join(ip) 195 | 196 | ip = list(map(lambda x: str(x), [e, f, g, h])) 197 | dst_addr = ".".join(ip) 198 | 199 | if dst_addr == THIS_NODE: 200 | print(time.strftime('%H:%M:%S'), 201 | f'| Received {msg_txt.decode()} from {src_addr}') 202 | else: 203 | print(time.strftime('%H:%M:%S'), 204 | f'| Forwarding {msg_txt.decode()} from {src_addr} to {dst_addr} via {ROUTING_TABLE[dst_addr][1]}') 205 | send_hello(msg_txt.decode(), src_addr, dst_addr) 206 | 207 | 208 | def send_hello(msg_txt, src_node, dst_node): 209 | """Send a message""" 210 | sock = socket(AF_INET, SOCK_DGRAM) 211 | sock.bind((THIS_NODE, PORT)) 212 | 213 | msg = format_hello(msg_txt, src_node, dst_node) 214 | 215 | port_id = ROUTING_TABLE[dst_node][1].split(".")[-1] 216 | 217 | sock.sendto(msg, (ROUTING_TABLE[dst_node][1], PORT+int(port_id))) 218 | sock.close() 219 | 220 | 221 | def print_status(): 222 | """Print status""" 223 | 224 | print('{:^20}{:^10}{:^20}'.format('Host', "Cost", "Via")) 225 | for node in ROUTING_TABLE: 226 | print('{:^20}{:^10}{:^20}'.format( 227 | node, ROUTING_TABLE[node][0], ROUTING_TABLE[node][1])) 228 | 229 | 230 | def main(args: list): 231 | """Router main loop""" 232 | 233 | print(time.strftime("%H:%M:%S"), "| Router", THIS_NODE, "here") 234 | 235 | read_file(args[1]) 236 | 237 | with socket(AF_INET, SOCK_DGRAM) as server: 238 | print(time.strftime('%H:%M:%S'), 239 | f'| Binding to {THIS_NODE}:{PORT+int(HOST_ID)}') 240 | server.bind((THIS_NODE, PORT+int(HOST_ID))) 241 | 242 | INPUTS = [server] 243 | 244 | print(time.strftime('%H:%M:%S'), 245 | f'| Listening on {THIS_NODE}:{PORT+int(HOST_ID)}') 246 | 247 | time.sleep(random.randint(2, 8)) 248 | 249 | for n in NEIGHBORS: 250 | send_update(n) 251 | 252 | print_status() 253 | 254 | while INPUTS: 255 | readable, writable, exceptional = select.select( 256 | INPUTS, [], [], TIMEOUT) 257 | update_vector = False 258 | 259 | if random.randint(0, 1000000) < 10: 260 | time.sleep(random.randint(2, 8)) 261 | send_msg = random.choice(MESSAGES) 262 | send_to = random.choice(list(ROUTING_TABLE.keys())) 263 | send_hello(send_msg, THIS_NODE, send_to) 264 | print(time.strftime('%H:%M:%S'), 265 | f'| Sending {send_msg} to {send_to} via {ROUTING_TABLE[send_to][1]}') 266 | 267 | for r in readable: 268 | 269 | recv, addr = server.recvfrom(1024) 270 | if recv: 271 | msg_type = int.from_bytes(recv[0:1], byteorder='big') 272 | if msg_type == 0: 273 | update_vector = parse_update(recv, addr[0]) 274 | if update_vector: 275 | time.sleep(random.randint(2, 8)) 276 | print(time.strftime('%H:%M:%S'), 277 | f'| Table updated with information from {addr[0]}') 278 | print_status() 279 | elif msg_type == 1: 280 | parse_hello(recv) 281 | else: 282 | print(time.strftime('%H:%M:%S'), 283 | f'| Received unexpected message from {addr}. Message will be ignored.') 284 | 285 | if update_vector: 286 | for n in NEIGHBORS: 287 | send_update(n) 288 | 289 | for i in INPUTS: 290 | CONNECTED.add(i.getsockname()[0]) 291 | 292 | NEW_CONNECTIONS = NEIGHBORS - CONNECTED 293 | 294 | for n in NEW_CONNECTIONS: 295 | send_update(n) 296 | 297 | 298 | if __name__ == "__main__": 299 | main(sys.argv) 300 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/router_4.py: -------------------------------------------------------------------------------- 1 | """Router implementation using UDP sockets""" 2 | #!/usr/bin/env python3 3 | # encoding: UTF-8 4 | 5 | 6 | import os 7 | import random 8 | import select 9 | from socket import socket, AF_INET, SOCK_DGRAM 10 | import struct 11 | import sys 12 | import time 13 | 14 | HOST_ID = os.path.splitext(__file__)[0].split("_")[-1] 15 | THIS_NODE = f"127.0.0.{HOST_ID}" 16 | PORT = 4300 17 | NEIGHBORS = set() 18 | ROUTING_TABLE = {} 19 | TIMEOUT = 5 20 | MESSAGES = [ 21 | "Cosmic Cuttlefish", 22 | "Bionic Beaver", 23 | "Xenial Xerus", 24 | "Trusty Tahr", 25 | "Precise Pangolin" 26 | ] 27 | 28 | CONNECTED = set() 29 | NEW_CONNECTIONS = set() 30 | 31 | # MINE = {} # TODO 32 | # NEIGHBORS_EXTENDED = {} # TODO 33 | 34 | INPUTS = [] 35 | 36 | 37 | def read_file(filename: str) -> None: 38 | """Read config file""" 39 | 40 | # Can be as simple as 41 | with open(filename, "r") as f: 42 | lines = f.readlines() 43 | found = False 44 | for line in lines: 45 | line = line.split() 46 | if len(line) == 1: 47 | if line[0] == THIS_NODE: 48 | found = True 49 | elif found == True: 50 | found = False 51 | elif len(line) == 2 and found: 52 | if found: 53 | ROUTING_TABLE.update({line[0]: [int(line[1]), line[0]]}) 54 | NEIGHBORS.add(line[0]) 55 | 56 | # This is way more complicated - meant for Dijkstra's comparison with Distance Vector # TODO 57 | """ skip = False 58 | current_neighbor = "" 59 | 60 | # This is more complicated than it should be - but I was debugging and some of the stuff here is used later for doulbe checking results with Dijkstras 61 | with open(filename, "r") as f: 62 | for line in f: 63 | line = line.split() 64 | 65 | if len(line) == 0: 66 | skip = False 67 | continue 68 | elif len(line) == 1: 69 | if line[0] == THIS_NODE: 70 | skip = True 71 | continue 72 | current_neighbor = line[0] 73 | elif len(line) == 2: 74 | if skip: 75 | NEIGHBORS.add(line[0]) 76 | MINE[line[0]] = line[1] 77 | ROUTING_TABLE[line[0]] = [line[1], line[0]] 78 | continue 79 | if not current_neighbor in NEIGHBORS_EXTENDED: 80 | NEIGHBORS_EXTENDED[current_neighbor] = [] 81 | NEIGHBORS_EXTENDED[current_neighbor].append( 82 | [line[0], line[1]]) 83 | 84 | # print(MINE) 85 | # print(NEIGHBORS) 86 | # print(NEIGHBORS_EXTENDED) 87 | 88 | # print(THIS_NODE) 89 | 90 | # print(ROUTING_TABLE) 91 | 92 | # Simulating Distance Vector Algorithm with Dijkstras Algorithm 93 | 94 | for neighbor in NEIGHBORS: 95 | neighbor_cost = int(MINE[neighbor]) 96 | # print("\t Neighbor:", neighbor, "cost:", neighbor_cost) 97 | for node in NEIGHBORS_EXTENDED[neighbor]: 98 | if THIS_NODE == node[0]: 99 | continue 100 | 101 | hop_cost = int(node[1]) + neighbor_cost 102 | 103 | if (not node[0] in ROUTING_TABLE) or (int(ROUTING_TABLE[node[0]][0]) > hop_cost): 104 | ROUTING_TABLE[node[0]] = [hop_cost, neighbor] 105 | 106 | # print("\t\t ", node[0], "cost", hop_cost) 107 | 108 | # break 109 | # print(NEIGHBORS_EXTENDED[neighbor]) 110 | 111 | # print(ROUTING_TABLE) """ 112 | 113 | 114 | def format_update(): 115 | """Format update message""" 116 | 117 | msg = bytearray() 118 | msg_type = 0 119 | msg.extend(msg_type.to_bytes(1, byteorder='big')) 120 | 121 | for dest, cost in ROUTING_TABLE.items(): 122 | a, b, c, d = tuple(map(lambda x: int(x), dest.split("."))) 123 | cost = int(cost[0]) 124 | msg_bytes = struct.pack("bbbbb", a, b, c, d, cost) 125 | msg.extend(msg_bytes) 126 | 127 | return msg 128 | 129 | 130 | def parse_update(msg, neigh_addr): 131 | """Update routing table""" 132 | 133 | msg = msg[1:] 134 | update = False 135 | 136 | for val in range(0, len(msg), 5): 137 | if len(msg) < 5: 138 | break 139 | temp = msg[val:val+5] 140 | 141 | values = struct.unpack('BBBBB', temp) 142 | values = list(map(lambda x: str(x), values)) 143 | ip = ".".join(values[:4]) 144 | cost = int(values[4]) 145 | 146 | if ip in ROUTING_TABLE: 147 | if ROUTING_TABLE[ip][0] > int(cost + ROUTING_TABLE[neigh_addr][0]): 148 | ROUTING_TABLE[ip] = [ 149 | (cost + int(ROUTING_TABLE[neigh_addr][0])), ROUTING_TABLE[neigh_addr][1]] 150 | update = True 151 | elif ip == THIS_NODE: 152 | continue 153 | else: 154 | ROUTING_TABLE[ip] = [ 155 | (cost + int(ROUTING_TABLE[neigh_addr][0])), neigh_addr] 156 | update = True 157 | 158 | return update 159 | 160 | 161 | def send_update(node): 162 | """Send update""" 163 | sock = socket(AF_INET, SOCK_DGRAM) 164 | sock.bind((THIS_NODE, PORT)) 165 | msg = format_update() 166 | 167 | port_id = node.split(".")[-1] 168 | table = format_update() 169 | 170 | sock.sendto(msg, (node, PORT+int(port_id))) 171 | sock.close() 172 | 173 | 174 | def format_hello(msg_txt, src_node, dst_node): 175 | """Format hello message""" 176 | msg_type = 1 177 | 178 | a, b, c, d = tuple(map(lambda x: int(x), src_node.split("."))) 179 | e, f, g, h = tuple(map(lambda x: int(x), dst_node.split("."))) 180 | 181 | rest = bytes(msg_txt, "utf-8") 182 | msg = struct.pack("bbbbbbbbb{}s".format( 183 | len(rest)), msg_type, a, b, c, d, e, f, g, h, rest) 184 | 185 | return msg 186 | 187 | 188 | def parse_hello(msg): 189 | """Send the message to an appropriate next hop""" 190 | msg_type, a, b, c, d, e, f, g, h, msg_txt = struct.unpack( 191 | 'bbbbbbbbb{}s'.format(len(msg[9:])), msg) 192 | 193 | ip = list(map(lambda x: str(x), [a, b, c, d])) 194 | src_addr = ".".join(ip) 195 | 196 | ip = list(map(lambda x: str(x), [e, f, g, h])) 197 | dst_addr = ".".join(ip) 198 | 199 | if dst_addr == THIS_NODE: 200 | print(time.strftime('%H:%M:%S'), 201 | f'| Received {msg_txt.decode()} from {src_addr}') 202 | else: 203 | print(time.strftime('%H:%M:%S'), 204 | f'| Forwarding {msg_txt.decode()} from {src_addr} to {dst_addr} via {ROUTING_TABLE[dst_addr][1]}') 205 | send_hello(msg_txt.decode(), src_addr, dst_addr) 206 | 207 | 208 | def send_hello(msg_txt, src_node, dst_node): 209 | """Send a message""" 210 | sock = socket(AF_INET, SOCK_DGRAM) 211 | sock.bind((THIS_NODE, PORT)) 212 | 213 | msg = format_hello(msg_txt, src_node, dst_node) 214 | 215 | port_id = ROUTING_TABLE[dst_node][1].split(".")[-1] 216 | 217 | sock.sendto(msg, (ROUTING_TABLE[dst_node][1], PORT+int(port_id))) 218 | sock.close() 219 | 220 | 221 | def print_status(): 222 | """Print status""" 223 | 224 | print('{:^20}{:^10}{:^20}'.format('Host', "Cost", "Via")) 225 | for node in ROUTING_TABLE: 226 | print('{:^20}{:^10}{:^20}'.format( 227 | node, ROUTING_TABLE[node][0], ROUTING_TABLE[node][1])) 228 | 229 | 230 | def main(args: list): 231 | """Router main loop""" 232 | 233 | print(time.strftime("%H:%M:%S"), "| Router", THIS_NODE, "here") 234 | 235 | read_file(args[1]) 236 | 237 | with socket(AF_INET, SOCK_DGRAM) as server: 238 | print(time.strftime('%H:%M:%S'), 239 | f'| Binding to {THIS_NODE}:{PORT+int(HOST_ID)}') 240 | server.bind((THIS_NODE, PORT+int(HOST_ID))) 241 | 242 | INPUTS = [server] 243 | 244 | print(time.strftime('%H:%M:%S'), 245 | f'| Listening on {THIS_NODE}:{PORT+int(HOST_ID)}') 246 | 247 | time.sleep(random.randint(2, 8)) 248 | 249 | for n in NEIGHBORS: 250 | send_update(n) 251 | 252 | print_status() 253 | 254 | while INPUTS: 255 | readable, writable, exceptional = select.select( 256 | INPUTS, [], [], TIMEOUT) 257 | update_vector = False 258 | 259 | if random.randint(0, 1000000) < 10: 260 | time.sleep(random.randint(2, 8)) 261 | send_msg = random.choice(MESSAGES) 262 | send_to = random.choice(list(ROUTING_TABLE.keys())) 263 | send_hello(send_msg, THIS_NODE, send_to) 264 | print(time.strftime('%H:%M:%S'), 265 | f'| Sending {send_msg} to {send_to} via {ROUTING_TABLE[send_to][1]}') 266 | 267 | for r in readable: 268 | 269 | recv, addr = server.recvfrom(1024) 270 | if recv: 271 | msg_type = int.from_bytes(recv[0:1], byteorder='big') 272 | if msg_type == 0: 273 | update_vector = parse_update(recv, addr[0]) 274 | if update_vector: 275 | time.sleep(random.randint(2, 8)) 276 | print(time.strftime('%H:%M:%S'), 277 | f'| Table updated with information from {addr[0]}') 278 | print_status() 279 | elif msg_type == 1: 280 | parse_hello(recv) 281 | else: 282 | print(time.strftime('%H:%M:%S'), 283 | f'| Received unexpected message from {addr}. Message will be ignored.') 284 | 285 | if update_vector: 286 | for n in NEIGHBORS: 287 | send_update(n) 288 | 289 | for i in INPUTS: 290 | CONNECTED.add(i.getsockname()[0]) 291 | 292 | NEW_CONNECTIONS = NEIGHBORS - CONNECTED 293 | 294 | for n in NEW_CONNECTIONS: 295 | send_update(n) 296 | 297 | 298 | if __name__ == "__main__": 299 | main(sys.argv) 300 | -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/routing.apng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/8-Distance Vector Routing Protocol/routing.apng -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/routing_capture.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/8-Distance Vector Routing Protocol/routing_capture.pcapng -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/routingfinal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/8-Distance Vector Routing Protocol/routingfinal.png -------------------------------------------------------------------------------- /8-Distance Vector Routing Protocol/trivial_routing_protocol.lua: -------------------------------------------------------------------------------- 1 | -- Trivial Routing Protocol dissector 2 | -- Implemented by Roman Yasinovskyy, 2018 3 | -- Declare the protocol 4 | local routing_proto = Proto("trp", "Trivial Routing Protocol") 5 | -- Declare the fields 6 | local TYPES = { [0] = "UPDATE", [1] = "HELLO", [2] = "STATUS REQUEST", [3] = "STATUS RESPONSE" } 7 | local pf_msg_type = ProtoField.new("Message Type", "trp.msg_type", ftypes.UINT8, TYPES) 8 | local pf_src_addr = ProtoField.new("Source", "trp.src_addr", ftypes.IPv4) 9 | local pf_dst_addr = ProtoField.new("Destination", "trp.dst_addr", ftypes.IPv4) 10 | local pf_dst_cost = ProtoField.new("Path cost", "trp.dst_cost", ftypes.UINT8) 11 | local pf_hello_txt = ProtoField.new("Hello text", "trp.hello_txt", ftypes.STRING) 12 | routing_proto.fields = { pf_msg_type, pf_src_addr, pf_dst_addr, pf_dst_cost, pf_hello_txt } 13 | 14 | function routing_proto.dissector(buffer, packet, tree) 15 | packet.cols.protocol = "TRP" 16 | 17 | local msg_type = buffer(0, 1):uint() 18 | local subtree = tree:add(routing_proto, buffer()) 19 | subtree:add(pf_msg_type, buffer(0, 1)) 20 | idx = 1 21 | packet_remaining = buffer:reported_length_remaining() - 1 22 | if msg_type == 0 then 23 | r = 0 24 | subtree = subtree:add(buffer(idx, packet_remaining), "Distance Vector") 25 | while packet_remaining > 0 do 26 | local record = subtree:add(buffer(idx, 5), "Record " .. r) 27 | record:add(pf_dst_addr, buffer:range(idx, 4)) 28 | idx = idx + 4 29 | packet_remaining = packet_remaining - 4 30 | record:add(pf_dst_cost, buffer(idx, 1)) 31 | idx = idx + 1 32 | packet_remaining = packet_remaining - 1 33 | r = r + 1 34 | end 35 | elseif msg_type == 1 then 36 | subtree = subtree:add(buffer(idx, packet_remaining), "Hello Message") 37 | subtree:add(pf_src_addr, buffer:range(idx, 4)) 38 | idx = idx + 4 39 | packet_remaining = packet_remaining - 4 40 | subtree:add(pf_dst_addr, buffer:range(idx, 4)) 41 | idx = idx + 4 42 | packet_remaining = packet_remaining - 4 43 | subtree:add(pf_hello_txt, buffer:range(idx, packet_remaining)) 44 | idx = idx + packet_remaining 45 | elseif msg_type == 2 then 46 | subtree = subtree:add(buffer(idx, packet_remaining), "Request Source") 47 | subtree:add(pf_src_addr, buffer:range(idx, 4)) 48 | idx = idx + 4 49 | elseif msg_type == 3 then 50 | subtree:add(pf_dst_addr, buffer:range(idx, 4)) 51 | idx = idx + 4 52 | packet_remaining = packet_remaining - 4 53 | r = 0 54 | subtree = subtree:add(buffer(idx, packet_remaining), "Distance Vector") 55 | while packet_remaining > 0 do 56 | local record = subtree:add(buffer(idx, 5), "Record " .. r) 57 | record:add(pf_dst_addr, buffer:range(idx, 4)) 58 | idx = idx + 4 59 | packet_remaining = packet_remaining - 4 60 | record:add(pf_dst_cost, buffer(idx, 1)) 61 | idx = idx + 1 62 | packet_remaining = packet_remaining - 1 63 | r = r + 1 64 | end 65 | end 66 | return idx 67 | end 68 | -- Load the udp.port table and register our protocol to handle port 4300 69 | udp_table = DissectorTable.get("udp.port") 70 | udp_table:add(4300, routing_proto) 71 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework0/osmanHomework0.txt: -------------------------------------------------------------------------------- 1 | What does HTTP stand for? 2 | Hypertext Transfer Protocol 3 | 4 | What HTTP methods are there? 5 | Get, Post, Update, Delete... These are the mainly used ones 6 | 7 | What does DNS stand for? 8 | Domain Naming Service 9 | 10 | What is the latest version of IP? 11 | IP V6 12 | 13 | What does Wi-Fi stand for? 14 | Wireless Fidelity 15 | 16 | How does ping utility work? 17 | To ping a server and figure out a connection can be establihed or not 18 | 19 | What is the difference between telnet and ssh? 20 | Telnet is not secure/encrypted while ssh is 21 | 22 | What does nslookup do? 23 | Figures out ip addresses of a domain 24 | 25 | How does traceroute utility work? 26 | I am guessing here... This utility might be a pocket sniffer? 27 | 28 | What command display networking information on Linux? 29 | ipconfig 30 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework1/Question10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework1/Question10.png -------------------------------------------------------------------------------- /9-Homework Review Questions/homework1/Question7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework1/Question7.png -------------------------------------------------------------------------------- /9-Homework Review Questions/homework1/Question9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework1/Question9.png -------------------------------------------------------------------------------- /9-Homework Review Questions/homework1/osmanHomework1.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework1/osmanHomework1.docx -------------------------------------------------------------------------------- /9-Homework Review Questions/homework1/osmanHomework1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework1/osmanHomework1.pdf -------------------------------------------------------------------------------- /9-Homework Review Questions/homework10/homework10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework10/homework10.png -------------------------------------------------------------------------------- /9-Homework Review Questions/homework10/osmanHomework10.txt: -------------------------------------------------------------------------------- 1 | 1-Why does IP fragmentation happen? What network entity is responsible for the fragments reassembly? 2 | 3 | IP fragmentation happens if the maximum transmission unit of a given link is smaller than the size of the packet. Any packet going through IP fragmentation would have to be broken into smaller chunks, or fragments, in order to be able to pass through the MTU. Acording to RFC 791, the reassembly takes place at the destination internet protocol module in the destination host. 4 | 5 | 6 | 2-What problem(s) does NAT solve? Briefly describe any major shortcoming of this technology. 7 | 8 | NAT tries to solve the issue of the finite amount of IPv4 addresses available. This technology allows the router to act as the single IP address for all the connect devices preverving IPv4 addresses and providing some kind of a buffer to protect against hackers and melecious attacks. The protection happens as the router is standing between any kind of connection to any of the devices connected to that router. However, NAT can also be quite inconvenient as the connected devices' IP addresses change every so often, which can complicate things such as a connection between from one device to another specific device on the same network. NAT also have some common problems with gaming devices (PS, XBox, and Nitendo for example). 9 | 10 | 11 | 3-Consider sending a 2400-byte datagram into a link that has an MTU of 700 bytes. Suppose the original datagram is stamped with the identification number 422. How many fragments are generated? What are the values in the various fields in the IP datagram(s) generated related to fragmentation? 12 | 13 | Four fragments are generated - the packets will look like this: 14 | Packet 1: len=700, id=422, fragflag=1, offset=0 15 | Packet 2: len=700, id=422, fragflag=1, offset=85 16 | Packet 3: len=700, id=422, fragflag=1, offset=170 17 | Packet 4: len=360, id=422, fragflag=0, offset=255 18 | 19 | 20 | 4-Suppose datagrams are limited to 1,500 bytes (including header) between source Host A and destination Host B. Assuming a 20-byte IP header, how many datagrams would be required to send an MP3 consisting of 5 million bytes? Explain how you computed your answer. 21 | 22 | 1500 - 20 = 1480 bytes per datagram 23 | 5000000 / 1480 = 3378.378 24 | Therefore 3379 datagrams are required to send this MP3 25 | 26 | 27 | 5-ARIN's free pool of IPv4 address space was depleted on 24 September 2015. What does it mean for the future of Internet? 28 | 29 | This means the scarcity and the directly proportional the increase of prices for IPv4 addresses. This also can mean that IPv6 will, sooner or later, have to replace IPv4. ARIN's free pool of IPv4 address space being depleted means that at some point or another, an alternative has to be used, and luckily, that alternative, IPv6, is available - adopting to that alternative, IPv6, is the most practical way for the internet to prosper and continue its progressing. Also, IPv6 has improved security more than IPv4, besides solving the problem of the addresses scarcity. 30 | 31 | 32 | 6-Use ARIN's whois service to find IP address blocks allocated to Luther College and one other college/university. Can the whois service be used to determine precise geographical location of a specific IP address? Use IP Location Finder or a similar service to determine locations of Web servers of both colleges. 33 | 34 | Luther College 35 | 2620:DD:4000:: - 2620:DD:4000:FFFF:FFFF:FFFF:FFFF:FFFF 36 | 192.203.196.0 - 192.203.196.255 37 | 198.133.77.0 - 198.133.77.255 38 | 66.43.231.0 - 66.43.231.255 39 | 66.43.252.0 - 66.43.252.255 40 | 74.207.32.0 - 74.207.63.255 41 | Earlham College 42 | 159.28.0.0 - 159.28.63.255 43 | 44 | IP addresses can be used to find geographical locations to some relative extent of accuracy. Sometimes the estimates of the locations by IP addresses can be only hundred meters off while some other times it can be kilometers away from the location. 45 | 46 | 47 | 48 | 49 | Consider the attached network topology when answering questions 7-10: 50 | Assign network addresses to each of these six subnets (the assignment should take the form a.b.c.d/x), with the following constraints: 51 | All addresses must be allocated from 214.97.254.0/23; ignore the class of the address, /23 is a valid mask. 52 | Subnet A should have enough addresses to support 250 interfaces; 53 | Subnet B should have enough addresses to support 120 interfaces; 54 | Subnet C should have enough addresses to support 120 interfaces. 55 | Subnets D, E, and F should each be able to support two interfaces. 56 | 57 | 58 | 7-What are the network address, broadcast address, and mask of the network A? How many host addresses are allocated to this network? 59 | 60 | Network address: 214.97.254.0 61 | Mask: /24 62 | Host addresses: 214.97.254.1 - 214.97.254.254 63 | Broadcast address: 214.97.254.255 64 | Hosts: 254 65 | 66 | 67 | 8-What are the network address, broadcast address, and mask of the network B? How many host addresses are allocated to this network? 68 | 69 | Network address: 214.97.255.0 70 | Mask: /25 71 | Host addresses: 214.97.255.1 - 214.97.255.126 72 | Broadcast address: 214.97.255.127 73 | Hosts: 126 74 | 75 | 76 | 9-What are the network address, broadcast address, and mask of the network C? How many host addresses are allocated to this network? 77 | 78 | Network address: 214.97.255.128 79 | Mask: /25 80 | Host addresses: 214.97.255.129 - 214.97.255.254 81 | Broadcast address: 214.97.255.255 82 | Hosts: 126 83 | 84 | 85 | 10-What are the network addresses, broadcast addresses, and masks of the networks D, E, and F? How many host addresses are allocated to these networks? 86 | 87 | We ran out of addresses - and good practice solutions - to fulfill the of for networks D, E, and F. 88 | 89 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework2/osmanHomework2.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework2/osmanHomework2.docx -------------------------------------------------------------------------------- /9-Homework Review Questions/homework2/osmanHomework2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework2/osmanHomework2.pdf -------------------------------------------------------------------------------- /9-Homework Review Questions/homework3/CS430_DNS.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework3/CS430_DNS.pcapng -------------------------------------------------------------------------------- /9-Homework Review Questions/homework3/CS430_DNS.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework3/CS430_DNS.zip -------------------------------------------------------------------------------- /9-Homework Review Questions/homework3/osmanHomework3.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework3/osmanHomework3.docx -------------------------------------------------------------------------------- /9-Homework Review Questions/homework3/osmanHomework3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework3/osmanHomework3.pdf -------------------------------------------------------------------------------- /9-Homework Review Questions/homework4/captured.txt: -------------------------------------------------------------------------------- 1 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 2 | 0000 00 0c 29 2e 74 b0 00 50 56 f0 c0 ba 08 00 45 00 3 | 0010 00 7e 15 7b 00 00 80 11 84 4a ac 10 24 02 ac 10 4 | 0020 24 87 00 35 98 3b 00 6a e8 34 3b 98 81 80 00 01 5 | 0030 00 02 00 00 00 00 04 61 72 69 6e 03 6e 65 74 00 6 | 0040 00 1c 00 01 04 61 72 69 6e 03 6e 65 74 00 00 1c 7 | 0050 00 01 00 00 00 05 00 10 20 01 05 00 00 04 c0 00 8 | 0060 00 00 00 00 00 00 00 43 04 61 72 69 6e 03 6e 65 9 | 0070 74 00 00 1c 00 01 00 00 00 05 00 10 20 01 05 00 10 | 0080 00 04 c0 00 00 00 00 00 00 00 00 44 11 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework4/osmanHomework4.txt: -------------------------------------------------------------------------------- 1 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 2 | 0000 00 0c 29 2e 74 b0 00 50 56 f0 c0 ba 08 00 45 00 3 | 0010 00 7e 15 7b 00 00 80 11 84 4a ac 10 24 02 ac 10 4 | 0020 24 87 00 35 98 3b 00 6a e8 34 3b 98 81 80 00 01 5 | 0030 00 02 00 00 00 00 04 61 72 69 6e 03 6e 65 74 00 6 | 0040 00 1c 00 01 04 61 72 69 6e 03 6e 65 74 00 00 1c 7 | 0050 00 01 00 00 00 05 00 10 20 01 05 00 00 04 c0 00 8 | 0060 00 00 00 00 00 00 00 43 04 61 72 69 6e 03 6e 65 9 | 0070 74 00 00 1c 00 01 00 00 00 05 00 10 20 01 05 00 10 | 0080 00 04 c0 00 00 00 00 00 00 00 00 44 11 | 12 | Answer the following questions and submit your answers as a single file (txt, rtf, docx, or pdf). Explain your answers by using specific values (and their location) from the captured frame. 13 | 14 | 1-What is the sender's MAC address? 15 | Sender's Mac address: 00:50:56:f0:c0:ba 16 | Located in the 6 bytes between 6-0B, 00 50 56 f0 c0 ba, on the 0000 row. 17 | 18 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 19 | 0000 00 50 56 f0 c0 ba 20 | 21 | 22 | 2-What Network layer protocol is used? 23 | IPv4 is the network layer protocol used 24 | Located in the 20 bytes between OE on row 0000 and 01 on row 0020. 25 | 26 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 27 | 0000 45 00 28 | 0010 00 7e 15 7b 00 00 80 11 84 4a ac 10 24 02 ac 10 29 | 0020 24 87 30 | 31 | 32 | 3-What is the sender's Network address? 33 | Sender's address: 172.16.36.2 34 | Located in the 4 bytes between 0A and 0D on row 0010. 35 | 36 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 37 | 0000 38 | 0010 ac 10 24 02 39 | 40 | 41 | 4-What Transport layer protocol is used? 42 | UPD is the transport layer protocol used 43 | Located in the 8 bytes between 02 and 09 in row 0020. 44 | 45 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 46 | 0000 47 | 0010 48 | 0020 00 35 98 3b 00 6a e8 34 49 | 50 | 51 | 5-What is the size of the transport layer datagram/segment? 52 | 8 bytes between 02 and 09 in row 0020. 53 | 54 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 55 | 0000 56 | 0010 57 | 0020 00 35 98 3b 00 6a e8 34 58 | 59 | 60 | 6-What Application layer protocol is used? 61 | Domain Name System 62 | Located in the 98 bytes between 0A on row 0020 and 0B on row 0080 63 | 64 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 65 | 0000 66 | 0010 67 | 0020 3b 98 81 80 00 01 68 | 0030 00 02 00 00 00 00 04 61 72 69 6e 03 6e 65 74 00 69 | 0040 00 1c 00 01 04 61 72 69 6e 03 6e 65 74 00 00 1c 70 | 0050 00 01 00 00 00 05 00 10 20 01 05 00 00 04 c0 00 71 | 0060 00 00 00 00 00 00 00 43 04 61 72 69 6e 03 6e 65 72 | 0070 74 00 00 1c 00 01 00 00 00 05 00 10 20 01 05 00 73 | 0080 00 04 c0 00 00 00 00 00 00 00 00 44 74 | 75 | 7-Is it a request or response? 76 | Response 77 | Located in the 2 bytes between 0C and 0D on row 0020 78 | 79 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 80 | 0000 81 | 0010 82 | 0020 81 80 83 | 84 | 85 | 8-What kind of request is this? What was the question? 86 | AAAA request - find IPv6 address of arin.net 87 | Located in the 14 bytes between 06 or row 0030 and 03 on row 0040 88 | 89 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 90 | 0000 91 | 0010 92 | 0020 93 | 0030 04 61 72 69 6e 03 6e 65 74 00 94 | 0040 00 1c 00 01 95 | 96 | 97 | 9-What kind of response is this? Does it answer the question? How many answers are there? 98 | Type AAAA Response, IPv6 address and it answers the question, and there are 2 answers. 99 | First answer located in the 36 bytes between 04 on row 0040 and 07 or row 0060 100 | 101 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 102 | 0000 103 | 0010 104 | 0020 105 | 0030 106 | 0040 04 61 72 69 6e 03 6e 65 74 00 00 1c 107 | 0050 00 01 00 00 00 05 00 10 20 01 05 00 00 04 c0 00 108 | 0060 00 00 00 00 00 00 00 43 109 | 110 | The IPv6 address is: 2001:500:4:c000::43 111 | 112 | Located in the 16 bytes between 08 on row 0050 and 07 on row 0060 113 | 114 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 115 | 0000 116 | 0010 117 | 0020 118 | 0030 119 | 0040 120 | 0050 20 01 05 00 00 04 c0 00 121 | 0060 00 00 00 00 00 00 00 43 122 | 123 | -------------------------- 124 | 125 | Second answer located in the 36 bytes between 08 on row 0060 and 0B or row 0080 126 | 127 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 128 | 0000 129 | 0010 130 | 0020 131 | 0030 132 | 0040 133 | 0050 134 | 0060 04 61 72 69 6e 03 6e 65 135 | 0070 74 00 00 1c 00 01 00 00 00 05 00 10 20 01 05 00 136 | 0080 00 04 c0 00 00 00 00 00 00 00 00 44 137 | 138 | The IPv6 address is: 2001:500:4:c000::44 139 | 140 | Located in the 16 bytes between 0C on row 0070 and 0B or row 0080 141 | 142 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 143 | 0000 144 | 0010 145 | 0020 146 | 0030 147 | 0040 148 | 0050 149 | 0060 150 | 0070 20 01 05 00 151 | 0080 00 04 c0 00 00 00 00 00 00 00 00 44 152 | 153 | 154 | 10-Provide any additional details about the frame and its content. 155 | For the first response answer, the TTL (Time-to-live) is 5 seconds. 156 | 157 | Located in the 4 bytes between 02 and 05 on row 0050. 158 | 159 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 160 | 0000 161 | 0010 162 | 0020 163 | 0030 164 | 0040 165 | 0050 00 00 00 05 166 | 167 | 168 | For the second response answer, the TTL (Time-to-live) is 5 seconds. 169 | 170 | Located in the 4 bytes between 06 and 09 on row 0070. 171 | 172 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 173 | 0000 174 | 0010 175 | 0020 176 | 0030 177 | 0040 178 | 0050 179 | 0060 180 | 0070 00 00 00 05 181 | 182 | -------------------------- 183 | 184 | For the first response answer, the IPv6 address is: 2001:500:4:c000::43 185 | 186 | Located in the 16 bytes between 08 on row 0050 and 07 on row 0060 187 | 188 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 189 | 0000 190 | 0010 191 | 0020 192 | 0030 193 | 0040 194 | 0050 20 01 05 00 00 04 c0 00 195 | 0060 00 00 00 00 00 00 00 43 196 | 197 | 198 | For the second response answer, the IPv6 address is: 2001:500:4:c000::44 199 | 200 | Located in the 16 bytes between 0C on row 0070 and 0B or row 0080 201 | 202 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 203 | 0000 204 | 0010 205 | 0020 206 | 0030 207 | 0040 208 | 0050 209 | 0060 210 | 0070 20 01 05 00 211 | 0080 00 04 c0 00 00 00 00 00 00 00 00 44 212 | 213 | -------------------------- 214 | 215 | Besides that, usually when getting response back from the server, a pointer of c00c, called an offset, is usually present to help you guide us to the answers. However, instead of using this pointer, the responses are using the domain name instead of using the c00c pointer to lead us to the answers. -------------------------------------------------------------------------------- /9-Homework Review Questions/homework5/CHKSUM/IMG_20181008_112713371.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework5/CHKSUM/IMG_20181008_112713371.jpg -------------------------------------------------------------------------------- /9-Homework Review Questions/homework5/CHKSUM/IMG_20181008_112715917.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework5/CHKSUM/IMG_20181008_112715917.jpg -------------------------------------------------------------------------------- /9-Homework Review Questions/homework5/CHKSUM/IMG_20181008_113255589.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework5/CHKSUM/IMG_20181008_113255589.jpg -------------------------------------------------------------------------------- /9-Homework Review Questions/homework5/captured.txt: -------------------------------------------------------------------------------- 1 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 2 | 0000 00 50 56 f6 42 4e 00 0c 29 01 0f c7 08 00 45 00 3 | 0010 00 34 bd fe 40 00 40 11 be e5 c0 a8 9e 81 c0 a8 4 | 0020 9e 02 a4 31 00 35 00 20 be 06 1f 71 01 00 00 01 5 | 0030 00 00 00 00 00 00 02 66 62 03 63 6f 6d 00 00 01 6 | 0040 00 01 7 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework5/osmanHomework5.txt: -------------------------------------------------------------------------------- 1 | 1-What is the purpose of source and destination port numbers in a transport protocol? 2 | The purpose of source and destination port numbers is the binding of a specific process(application) with a specific source and destination ports, so that the process can send and receive data via the network on those ports. The OS takes care of transmitting the data in and out of the network, via the specific ports, to and from the processes listening and sending on those specific ports. Transport layer protocols, such as TCP and UDP, use source and destination ports to specify the source and destination processes that should send and receive the data in transportation. 3 | 4 | 5 | 2-What is the difference, if any, between UDP and TCP sockets? 6 | For TCP sockets' connections, the TCP stack is in charge of making sure that the data is being sent to the network and then being delivered to the receiver, and making suring the it is retransmitted until the receiver acknowledges this data. In addition to that, TCP connections are flow-control oriented, meaning that the data is transmitted at a suitable rate for the network connections of the sender and the receiver. TCP connections also make sure that the receiver receives the data only on time and receive it in the correct order too. 7 | 8 | For UDP sockets' connections, the data transmissions needs to be managed by the programmer, meaning that the programmer needs to take care of the lost and out of order packets, the flow control, and the fragmentation of the data into packets that can be transmitted over the network connections. UDP sockets simply mean sending and receiving data from and to addresses (and ports if specified) - the UDP sockets do not take charge of making sure the data gets sent or received correctly, this is the programmer's job when using UDP sockets. 9 | 10 | 11 | 3-What is the difference between error detection and correction? Which one is provided by UDP? 12 | Error detection is to check, via checksum, the integrity of the received data. TCP Transport layer dismisses any data that is bad/corrupted and goes on to do error correction. Error correction happens via automatic repeat request (ARQ) to retransmit the data and check its integrity -error detection- until it is the valid and correct data. UDP provides error detection via checksum integrity check. The UDP checksum is optional. For UDP, having no error correction, and even having error detection as optional, are some of the reasons why UDP is faster than TCP. 13 | 14 | 15 | 4-Currently checksum is calculated at various levels of the TCP/IP stack. Do you think it's necessary, or is it enough to do once? 16 | The checksum happens at multiple levels because there are multiple sources of errors, including losing data at the physical level, giving a reason for checksum to happen at the Ethernet level, however, the checksum on the Ethernet level is not perfect. IPv4 checks for the header checksum and leaves the rest for the Transportation layer, and sometimes the Application layer can have its own checksum too (like peer-to-peer applications). However, all of that is not necessary, and for example, in IPv6 the checksum was removed and left to be done over the Transportation layer. The layer of Ethernet is very old, and at the same time, the checksum happening on this level is very fast, so there is nothing wrong with leaving the Ethernet layer checksum. Between the checksum over the Ethernet layer and the checksum over the Transportation layer, we are confident that any bad/corrupted packets would be caught. 17 | 18 | 19 | 5-What do you think of QUIC? What problems does it solve? What are its advantages over traditional protocols? 20 | QUIC, a recently developed transportation protocol by Google, is an improvement over TCP. One of its main features is the acceleration of HTTP traffic while at the same time being more secure than TCP. To put in simpler words, QUIC is far more secure than TCP while being faster than TCP as well. QUIC does not require multiple connections, like TCP, because of the multiplexing exchanges. Looking at the improvements to speed and security, QUIC has major advantages when compared to traditional protocols. 21 | 22 | 23 | For questions 6-10 consider the following packet and read UDP Checksum HowTo. Note that first row (00-0F) and first column (0000-0040) are not part of the packet data. 24 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 25 | 0000 00 50 56 f6 42 4e 00 0c 29 01 0f c7 08 00 45 00 26 | 0010 00 34 bd fe 40 00 40 11 be e5 c0 a8 9e 81 c0 a8 27 | 0020 9e 02 a4 31 00 35 00 20 be 06 1f 71 01 00 00 01 28 | 0030 00 00 00 00 00 00 02 66 62 03 63 6f 6d 00 00 01 29 | 0040 00 01 30 | 31 | 32 | 6-What is the source IP address of this datagram in hexadecimal and dot-decimal notation? 33 | Hexadecimal: c0 a8 9e 81 34 | Dot-decimal: 192.168.158.129 35 | 36 | 37 | 7-What is the destination IP address of this datagram in hexadecimal and dot-decimal notation? 38 | Hexadecimal: c0 a8 9e 02 39 | Dot-decimal: 192.168.158.2 40 | 41 | 42 | 8-What is the length of this datagram? 43 | Looking at the packets, we can see that there is the following: 44 | 20 bytes for the IPv4 45 | 8 bytes for the UDP 46 | 24 bytes for the DNS 47 | 48 | However, a datagram is equal to UDP + Data = 8 bytes + 24 bytes = 32 bytes. This is equal to the value of the length in the UDP Datagram. 49 | 50 | Datagram length = 32 bytes. 51 | 52 | 53 | 9-What is the checksum of this datagram? 54 | 0xbe06 which Wireshark considers incorrect 55 | 0x4825 for the pseudo part, the UDP part, and the Data part - Wireshark says: (maybe caused by "UDP checksum offload"?) 56 | 57 | 58 | 10-Verify the checksum of this datagram. Show your work. 59 | Pseudo Bytes 60 | SRC IP: c0 a8 61 | 9e 81 62 | DST IP: c0 a8 63 | 9e 02 64 | 0+prot: 00 11 65 | 66 | UDP Bytes 67 | SRT PT: a4 31 68 | DST PT: 00 35 69 | Length: 00 20 70 | CHKSUM: 00 00 71 | 72 | Data Bytes: 73 | 1f 71 74 | 01 00 75 | 00 01 76 | 00 00 77 | 00 00 78 | 00 00 79 | 02 66 80 | 62 03 81 | 63 6f 82 | 6d 00 83 | 00 01 84 | 00 01 85 | 86 | ----------------------------------- 87 | 88 | In Python3: 89 | chk_pseudo = 0xc0a8 + 0x9e81 + 0xc0a8 + 0x9e02 + 0x0011 90 | hex(chk_pseudo) 91 | 92 | OUTPUT: '0x2bde4' 93 | 94 | chk_udp = 0xa431 + 0x0035 + 0x0020 + 0x0000 95 | hex(chk_udp) 96 | 97 | OUTPUT: '0xa486' 98 | 99 | chk_data = 0x1f71 + 0x0100 + 0x0001 + 0x0000 + 0x0000 + 0x0000 + 0x0266 + 0x6203 + 0x636f + 0x6d00 + 0x0001 + 0x0001 100 | hex(chk_data) 101 | 102 | OUTPUT: '0x1554c' 103 | 104 | 105 | chk_full = chk_pseudo + chk_udp + chk_data 106 | hex(chk_full) 107 | 108 | OUTPUT: '0x4b7b6' 109 | 110 | bin(chk_full) 111 | 112 | OUTPUT: '0b1001011011110110110' 113 | 114 | ----------------------------------- 115 | 116 | Add the following for overflow 117 | 1011011110110110 118 | 100 119 | ________________ 120 | 1011011110111010 121 | 122 | Doing one's compliment 123 | 124 | 0100100001000101 125 | 126 | Converting to Hexadecimal: 127 | 4845 128 | 129 | The calculated checksum is: 0x4845 130 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework6/osmanHomework6.txt: -------------------------------------------------------------------------------- 1 | 1- 2 | Question: Looking at the following packet, what kind of request is made? 3 | 4 | 0000 00 15 60 f6 88 00 a4 5e 60 ec 29 9d 08 00 45 00 ..`ö..¤^`ì)...E. 5 | 0010 00 b5 00 00 40 00 40 06 0c 69 0a 1c 14 2a 6b 14 .µ..@.@..i...*k. 6 | 0020 a4 80 cc f6 00 50 9d e8 dc e3 b4 20 38 a9 80 18 ¤.Ìö.P.èÜã´ 8©.. 7 | 0030 10 00 8b 6c 00 00 01 01 08 0a 30 05 9b 51 01 ff ...l......0..Q.ÿ 8 | 0040 7b e5 47 45 54 20 2f 56 33 2f 30 31 2f 31 30 36 {åGET /V3/01/106 9 | 0050 2e 36 2e 32 31 37 2e 31 37 32 2e 69 70 2f 20 48 .6.217.172.ip/ H 10 | 0060 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 68 TTP/1.1..Host: h 11 | 0070 74 74 70 2e 30 30 2e 61 2e 73 6f 70 68 6f 73 78 ttp.00.a.sophosx 12 | 0080 6c 2e 6e 65 74 0d 0a 55 73 65 72 2d 41 67 65 6e l.net..User-Agen 13 | 0090 74 3a 20 53 58 4c 2f 33 2e 31 0d 0a 41 63 63 65 t: SXL/3.1..Acce 14 | 00a0 70 74 3a 20 2a 2f 2a 0d 0a 43 6f 6e 6e 65 63 74 pt: */*..Connect 15 | 00b0 69 6f 6e 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d ion: Keep-Alive. 16 | 00c0 0a 0d 0a ... 17 | 18 | Answer: GET request in an HTTP protocol packet. 19 | 20 | 21 | 2- 22 | Question: Looking at the following packet, what is the source IP address in hexadecimal and dot-decimal notation? 23 | 24 | 0000 a4 5e 60 ec 29 9d 00 15 60 f6 88 00 08 00 45 00 25 | 0010 00 78 6a 69 00 00 7d 11 2f f7 c0 cb c4 03 0a 1c 26 | 0020 14 2a 00 35 d8 51 00 64 89 97 be ef 81 83 00 01 27 | 0030 00 00 00 01 00 01 05 64 65 62 75 67 07 6f 70 65 28 | 0040 6e 64 6e 73 03 63 6f 6d 00 00 10 00 01 c0 12 00 29 | 0050 06 00 01 00 00 01 86 00 22 05 61 75 74 68 31 c0 30 | 0060 12 03 6e 6f 63 c0 12 5b bf c3 61 00 00 40 00 00 31 | 0070 00 08 00 00 10 00 00 00 00 0a 00 00 00 29 0f a0 32 | 0080 00 00 00 00 00 00 33 | 34 | Answer: 35 | Hexadecimal: c0 cb c4 03 36 | Dot-decimal: 192.203.196.3 37 | 38 | 39 | 3- 40 | Question: Convert 1995 of base 10 to its binary (base 2) form. 41 | 42 | Answer: 43 | 1995/2 = 997r1 44 | 997/2 = 498r1 45 | 498/2 = 249r0 46 | 249/2 = 124r1 47 | 124/2 = 62r0 48 | 62/2 = 31r0 49 | 31/2 = 15r1 50 | 15/2 = 7r1 51 | 7/2 = 3r1 52 | 3/2 = 1r1 53 | 1/2 = 0r1 54 | 55 | 11111001011 is the base 2 form of 1995 of base 10 form. 56 | 57 | 58 | 4- 59 | Question: What is the difference between the transport layer and the application layer in terms responsabilties? 60 | 61 | Answer: 62 | The application layer is responsible for allowing access to network resources, establishing, managing, and terminating sessions. It is also responsible for translation, encrypt/decryt(in case of the presence of encryption), and compression/decompression of data. 63 | 64 | The transportation layer is is responsible for providing reliable process-to-process message delivery and error recovery. The process-to-process message delivery is supported by specifying port numbers for socket connections. The error recovery for UDP is simple error detection while for TCP is error detection and error correction. 65 | 66 | 67 | 5- 68 | Question: Convert 1995 of base 5 to its decimal form. 69 | 70 | Answer: 71 | 5^0 * 5 + 5^1 * 9 + 5^2 * 9 + 5^3 * 1 = 400 72 | 73 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework7/CS430_TCP.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework7/CS430_TCP.pcap -------------------------------------------------------------------------------- /9-Homework Review Questions/homework7/osmanHomework7.txt: -------------------------------------------------------------------------------- 1 | 1-What is the IP address and the port number used by the client? Is the client downloading or uploading data? (Hint: look for the amount of data that that flows in either direction) 2 | IP Address: 192.168.1.102 3 | Source Port: 1161 4 | The client is uploading as the destination is only sending acknowledgements back. 5 | 6 | 7 | 2-What is the content of the file in transit? 8 | The content of the file is Alice in Wonderland 9 | File name is "alice.txt" 10 | 11 | 12 | 3-What are the relative and real sequence numbers in the SYN/ACK segment sent by the server? (Hint: read TCP_Relative_Sequence_Numbers - The Wireshark Wiki to display real, not relative sequence numbers) 13 | The relative sequence number is: 0 14 | The real sequence number is: 883061785 15 | 16 | 17 | 4-What HTTP method is the client using? Look for HTTP messages containing either GET or POST method. 18 | The client is using POST. Packet number 4, sent by the client, has a POST method. 19 | 20 | 21 | 5-What is the throughput (bytes transferred per unit time) for the TCP connection? Explain how you calculated this value. 22 | By looking at the Conversions tool from the Statistics menu, there are 176k bytes that get transmitted in 5.6511 seconds. Calculating throughput can be done by adding length and then dividing by the time of the last meaningful packet: total bytes / time. 23 | 176000 / 5.6511 = 31144.379 bytes per second 24 | 25 | 26 | 6-Was this connection properly closed by the client? 27 | By looking at the expression: tcp.flags.fin eq 1, the connection was not closed properly. 28 | 29 | 30 | 7-What is the minimum amount of available buffer space advertised at the received for the entire trace? Does the lack of receiver buffer space ever throttle the sender? 31 | At packet number 2, the minimum amount of available buffer space advertised by the receiver was found with a size of 5840. The sender is never throttled as there little to no time gaps between packets transmitted, and the windows is always larger than the data sent. Also, the window is never exceeded by the sender. 32 | 33 | 34 | 8-Many packets from the server have some random padding added at the end (## 6, 9, 12 etc.) Why packets need to be at least 60 bytes long? 35 | The standard minimum Ethernet frame is of size 64 bytes, and since the FCS adds extra 4 bytes upons sending, then 60 bytes is the smallest acceptable packet size. 36 | 37 | 38 | 9-Packets #3 and #206 are smaller than 60 bytes and yet not padded. Why is that? 39 | Packets, created by the sending machine we are capturing from(your machine, Dr. Roman), are not padded until right before transmission takes place, and wireshark grabs those packets for us before transmission. When those packets are sent, they will be at least 60 bytes in size. Right now, they have not been padded because they were captured by Wireshark before transmission. 40 | 41 | 42 | 10-What is the indication that the file was transmitted successfully? 43 | At packet #203, a 200 response code can be found, indicating that the file was transmitted successfully. 44 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework8/osmanHomework8.txt: -------------------------------------------------------------------------------- 1 | 1-Suppose Host A sends two sequential TCP segments to Host B over TCP. Sequence number of the first segment is 430, its size is 30 bytes. What is the sequence number of the second segment? 2 | 460 3 | 4 | 5 | 2-Suppose Host A sends two sequential TCP segments to Host B over TCP. Sequence number of the first segment is 430, its size is 30 bytes. First packet is lost, but the second is delivered successfully. What is the acknowledgement number sent from Host B to Host A in the ACK message? 6 | 430 7 | 8 | 9 | 3-Is TCP closer to the Go-Back-N or Selective Repeat algorithm? Explain your answer. 10 | TCP is closer to Selective Repeat rather than G-Back-N because when a packet is lost due to congestion, under the TCP protocol, the sender does not have to retransmit all the unacknowledged packets bur rather retransmit the oldest unacknowledged packet, which is a Selective Repeat algorithm. Go-Back-N, on the other hand, would retransmit all the packets from the point of the oldeest unacknowledged packet. 11 | 12 | 13 | 4-Briefly explain the need for 3 (three) messages in the TCP handshake. Could we use fewer messages? Should we use more? 14 | The three-way handshakes makes TCP a connection-oriented protocol. The number three, for the three-way handshake, was not chosen abruptly but rather to serve a purpose of having both the sender and the receiver synchronize and acknowledge each others Initial Sequence number, so we should not user fewer, and we should not use more because, unless we are transmitting data, the connection has been established and the SYN, SYN/ACK, and ACK process has been achieved by this three-way handshake. I think this is the best way to establish connection, no more and no less of a handshake is needed. 15 | 16 | 17 | 5-What is the difference between TCP Reno and Tahoe? 18 | Tahoe, at congestion, treats triple duplicate ACKs the same way it treats a timout, and when either happen it performs a fast retransmit, reduces the congestion window to 1, and enters slow-start. However, for Reno, when a triple duplicate ACKs are received, the congestion window gets halfed, and performs a fast retransmit and then start recovry. In case of a timeout, Reno will enter a slow-start the same way Tahoe does. 19 | 20 | 21 | 6-Consider the following packet (part of a Telnet session). What are the expected sequence number and acknowledgment number in the response packet (sent from server to client)? 22 | Seq: 4168689280 23 | Ack: 3191800522 24 | 25 | 26 | 7-What are the time intervals of TCP slow start? 27 | The slow start times are from 1 to 6 and from 23 to 26. 28 | 29 | 30 | 8-What causes window shrinkage after the 16th round? 31 | Triple duplicate ACKs. 32 | 33 | 34 | 9-What causes window shrinkage after the 22nd round? 35 | A timeout. 36 | 37 | 38 | 10-During what transmission round is the 70th segment sent? 39 | The 70th segment is sent in the 7th transmission round. 40 | 41 | ----- 42 | 43 | General notes to myself: 44 | TCP: makes sure that both the Client and the Server are ready for the data transmissions, and helps in figuring out congestional control and flow control. 45 | -------------------------------------------------------------------------------- /9-Homework Review Questions/homework8/tcp_congestion_reno.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework8/tcp_congestion_reno.PNG -------------------------------------------------------------------------------- /9-Homework Review Questions/homework8/telnet_capture_seq_ack.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAhmadOsman/ComputerNetworks/b4ec488d79095d95edc203ef10626eb83d8703f1/9-Homework Review Questions/homework8/telnet_capture_seq_ack.PNG -------------------------------------------------------------------------------- /9-Homework Review Questions/homework9/osmanHomework9.txt: -------------------------------------------------------------------------------- 1 | 1-What is the difference between forwarding and routing? 2 | Forwarding refers to a datagram being sent from one device to another in a path to the destination. Forwarding is also done within the routing process. 3 | 4 | Routing is a network-wide process to make a decision about the end-to-end paths that packets should take starting from the source until reaching destination. 5 | 6 | 7 | 2-What are the advantages of circuit switching over packet switching and vice versa? 8 | A network that uses circuit switching guarantees a specific amount of end-to-end bandwidth for the duration of the connection, while most of the networks - even the Internet - that use packet switching cannot guarantee a specific amount of end-to-end bandwith for the duration of the connection. Circuit switching guarentees quality. 9 | 10 | However, packet switching is relatively cheap compared to circuit switching - that requires a dedicated circuit. Also, packet switching would maximize the usage of a circuit, while for circuit switching, the unavailability of circuts means adding connections will not be possible. Usage efficiency is low in circuit switching relative to packet switching. 11 | 12 | 13 | 3-What network service (delivery, delay, bandwidth, security) do you think is the most important? Explain your answer. 14 | I think that bandwith is the most important network service. It is the most essential factor when it comes to improving the experience of internet surfing and multimedia streaming - this does not only apply to entertainment but can also apply to large data being transfered from one point to another for scientific reasons, or to compress/extract data at specific data centers like what they do at Mayo Clinic with their daily large produced genomes. Bandwidth makes our experience smoother and opens up more possiblities for an even more smoother and better experience. 15 | 16 | 17 | 4-What is a router's switching fabric? 18 | Basically, a router's switching fabric connects its own input and output ports. It controls the trafic of the connections and transfers packets from one point to another where they should be. 19 | 20 | 21 | 5-It was noted that the maximum queuing delay is (n–1)D if the switching fabric is n times faster than the input line rates. Suppose that all packets are of the same length, n packets arrive at the same time to the n input ports, and all n packets want to be forwarded to different output ports. What is the maximum delay for a packet for the memory, bus, and crossbar switching fabrics? To answer the question consider the max number of packets that can be processed using each switching fabric (1 or n). 22 | The maximum delay for a packet for the memory switching fabric is (n-1)D as the memory only allows a one at a time packet transfer. 23 | 24 | The maximum delay for a packet for the bus switching fabric is (n-1)D as the bus only allows a one at a time packet transfer. 25 | 26 | The maximum delay for a packet for the crossbar switching fabric is 0 as crossbar utilizies parallel prcoessing. 27 | 28 | 29 | 6-Briefly explain the idea of the longest prefix match in forwarding. 30 | The longest prefix match is used by routers in order to figure out which ip should have a packet forwarded to when the destination's IP prefix has multiple possible matches within a table of outputs. The router querys for and determines the IP address with the longest matching prefix when compared to the packet's destination IP. 31 | 32 | 33 | Use the following table when answering questions 7-10: 34 | 35 | Destination Address Range Start Destination Address Range End Link Interface 36 | 37 | 11100000 00000000 00000000 00000000 11100000 00111111 11111111 11111111 0 38 | 39 | 11100000 01000000 00000000 00000000 11100000 01000000 11111111 11111111 1 40 | 41 | 11100000 01000001 00000000 00000000 11100001 01111111 11111111 11111111 2 42 | 43 | default default 3 44 | 45 | 7-Provide a forwarding table that has four entries, uses longest prefix matching, and forwards packets to the correct link interfaces. Remember that the table should use prefixes for routing. 46 | Prefix Match Link Interface 47 | 11100000 00xxxxxx xxxxxxxx xxxxxxxx 0 48 | 11100000 01000000 xxxxxxxx xxxxxxxx 1 49 | 1110000x xxxxxxxx xxxxxxxx xxxxxxxx 2 50 | 11100001 1xxxxxxx xxxxxxxx xxxxxxxx 3 51 | default 3 52 | 53 | 54 | 8-Describe how your forwarding table determines the appropriate link interface for a packet with the destination address of 11001000 10010001 01010001 01010101. 55 | Prefix matching for this destination address points to Link 3 56 | This is because the destination address starts with 11001000 1, which is Link 3. 57 | 58 | 59 | 9-Describe how your forwarding table determines the appropriate link interface for a packet with the destination address of 11100001 01000000 11000011 00111100. 60 | Prefix matching for this destination address points to Link 2 61 | This is because the destination address starts with 11001001 0, which is within the range of Link 2 and yet does not reach the starting range of Link 3; making this Link 2. 62 | 63 | 64 | 10-Describe how your forwarding table determines the appropriate link interface for a packet with the destination address of 11100001 10000000 00010001 01110111. 65 | Prefix matching for this destination address points to Link 3 66 | This is because the destination address starts with 11001000 1, which is Link 3. 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :spider_web: Computer Networks - CS430 :satellite: 2 | 3 | Client-Server, Sockets, DNS, TCP/IP, Web Server, Ping, Traceroute, Web Server, Routing, Top-Down Layered Model. 4 | 5 | Implementation of network applications, tools, services, and algorithms in Python. 6 | 7 | ## Projects Implemented 8 | 9 | I took this class in Fall 2018. Each folder is its own project with the implementation some Computer Networks concept/technique/algorithm. The projects are all in Python and can be walked through easily and each project has its own readme file with description. Projects include: 10 | 11 | 1. Simple Client and Server 12 | * Can be thought of as a basic socket example in Python. 13 | 14 | 2. A Bit Complicated Client and Server - Data Retrieval 15 | * Data retrieval using sockets. 16 | 17 | 3. DNS Resolver 18 | * Domain Name System Resolving. 19 | 20 | 4. DNS Server 21 | * Custom Domain Name System Server. 22 | 23 | 5. Web Server 24 | * Simple Web Server Implementation. 25 | 26 | 6. Ping Tool 27 | * Implementation of the infamous `ping`! 28 | 29 | 7. Traceroute Tool 30 | * Implementation of the infamous `traceroute`! 31 | 32 | 8. Distance Vector Routing Protocol 33 | * Implementation of a routing protocol using Distance Vector algorithm. 34 | 35 | 9. Review Questions 36 | * Homework review questions and their answers 37 | 38 | You can find projects and homework review questions and their answers in their corresponding branches, too. 39 | 40 | Feel free to clone this repository and to explore the projects. 41 | --------------------------------------------------------------------------------