From 885ce73d7ebdd762b95d880dac215ec47c664b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Dobrovoljc?= Date: Sun, 26 May 2024 18:10:13 +0200 Subject: [PATCH] LDN10 --- .gitignore | 1 + LDN10/.idea/.gitignore | 8 + LDN10/.idea/LDN10.iml | 10 ++ .../inspectionProfiles/profiles_settings.xml | 6 + LDN10/.idea/misc.xml | 7 + LDN10/.idea/modules.xml | 8 + LDN10/.idea/vcs.xml | 6 + LDN10/certs/clients.pem | 21 +++ LDN10/certs/janez_cert.crt | 21 +++ LDN10/certs/janez_private.key | 28 +++ LDN10/certs/server_cert.crt | 22 +++ LDN10/certs/server_private.key | 28 +++ LDN10/client.py | 107 ++++++++++++ LDN10/server.py | 165 ++++++++++++++++++ LDN9/dns-transfer.pcapng | Bin 0 -> 2204 bytes LDN9/screenshot.png | Bin 0 -> 31403 bytes 16 files changed, 438 insertions(+) create mode 100644 .gitignore create mode 100644 LDN10/.idea/.gitignore create mode 100644 LDN10/.idea/LDN10.iml create mode 100644 LDN10/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 LDN10/.idea/misc.xml create mode 100644 LDN10/.idea/modules.xml create mode 100644 LDN10/.idea/vcs.xml create mode 100644 LDN10/certs/clients.pem create mode 100644 LDN10/certs/janez_cert.crt create mode 100644 LDN10/certs/janez_private.key create mode 100644 LDN10/certs/server_cert.crt create mode 100644 LDN10/certs/server_private.key create mode 100644 LDN10/client.py create mode 100644 LDN10/server.py create mode 100644 LDN9/dns-transfer.pcapng create mode 100644 LDN9/screenshot.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/LDN10/.idea/.gitignore b/LDN10/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/LDN10/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/LDN10/.idea/LDN10.iml b/LDN10/.idea/LDN10.iml new file mode 100644 index 0000000..2c80e12 --- /dev/null +++ b/LDN10/.idea/LDN10.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/LDN10/.idea/inspectionProfiles/profiles_settings.xml b/LDN10/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/LDN10/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/LDN10/.idea/misc.xml b/LDN10/.idea/misc.xml new file mode 100644 index 0000000..32cefa8 --- /dev/null +++ b/LDN10/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/LDN10/.idea/modules.xml b/LDN10/.idea/modules.xml new file mode 100644 index 0000000..0c9003c --- /dev/null +++ b/LDN10/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/LDN10/.idea/vcs.xml b/LDN10/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/LDN10/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LDN10/certs/clients.pem b/LDN10/certs/clients.pem new file mode 100644 index 0000000..46af955 --- /dev/null +++ b/LDN10/certs/clients.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDizCCAnOgAwIBAgIUVPHhcVC0FfI3dsbcMAd0AEifmPMwDQYJKoZIhvcNAQEL +BQAwVTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFamFuZXowHhcNMjQw +NTI2MTU1ODQ4WhcNMjUwNTI2MTU1ODQ4WjBVMQswCQYDVQQGEwJBVTETMBEGA1UE +CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk +MQ4wDAYDVQQDDAVqYW5lejCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJd9GojC/SaEAUFopY1T6P/U2jZlBkYlGgLh4QWLOdwQr3O9SwbKovVaCANtFWBo +vXFaduJ2d19RaEI8CbffjONlYIGecppoeKXoSe9z7qSjQPvqnijcOVLyuLNsTM1U +mewsdDjVxa+ZjLNAkqU3QrKl28ms945nogYYvOYiyNelwb6VkCi+Sm+Gu7lDQytZ +OteFA9o3OLXP5tbPJT3AjRbXReuARHFy3Q6rburszyUfeT9XatQQ9zLWAlnU3w3X +a5vP7lJHLoF1NiVdbmE2H12LPR43Ima0zLfDOf8YpY2DQaENlNuMuJ4NRjUydI69 +kbonAaZNX196FmX03GHZUCUCAwEAAaNTMFEwHQYDVR0OBBYEFKE0+A+tQNYWGRff +dPhuISf1W7qvMB8GA1UdIwQYMBaAFKE0+A+tQNYWGRffdPhuISf1W7qvMA8GA1Ud +EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD0uiU+vjpLIRqshFBtzWJ7K +zRXb6zR1EGhbs63HRoTmW4s/YCuwZqdvmggd4j5a9RtFCIRO/4MjswrXV6rG0IiN +JXSb4ugz44oGUXsWboGHq3e8/pTJMq+cT4EO2t+irZ2qPe+jtZOJQwDU2NEJzkN6 +P+mcNR1I3pVfA+h1kLDspyv/6pbGQrfo0S8sJzetGYSFPBvEOagS6iHRlC7PwqrZ +gCADDHeewNksX8wGqiselXYQbiIrcGrb42fDgoX0aovEqUuux8Ukifm+DU1P9Hzv +FdMuT/pFNcwOXaETKN4oS4zMB/qGxqEnh3d7oItSBY3XjTgSORC25yf3irTWTww= +-----END CERTIFICATE----- diff --git a/LDN10/certs/janez_cert.crt b/LDN10/certs/janez_cert.crt new file mode 100644 index 0000000..46af955 --- /dev/null +++ b/LDN10/certs/janez_cert.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDizCCAnOgAwIBAgIUVPHhcVC0FfI3dsbcMAd0AEifmPMwDQYJKoZIhvcNAQEL +BQAwVTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFamFuZXowHhcNMjQw +NTI2MTU1ODQ4WhcNMjUwNTI2MTU1ODQ4WjBVMQswCQYDVQQGEwJBVTETMBEGA1UE +CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk +MQ4wDAYDVQQDDAVqYW5lejCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJd9GojC/SaEAUFopY1T6P/U2jZlBkYlGgLh4QWLOdwQr3O9SwbKovVaCANtFWBo +vXFaduJ2d19RaEI8CbffjONlYIGecppoeKXoSe9z7qSjQPvqnijcOVLyuLNsTM1U +mewsdDjVxa+ZjLNAkqU3QrKl28ms945nogYYvOYiyNelwb6VkCi+Sm+Gu7lDQytZ +OteFA9o3OLXP5tbPJT3AjRbXReuARHFy3Q6rburszyUfeT9XatQQ9zLWAlnU3w3X +a5vP7lJHLoF1NiVdbmE2H12LPR43Ima0zLfDOf8YpY2DQaENlNuMuJ4NRjUydI69 +kbonAaZNX196FmX03GHZUCUCAwEAAaNTMFEwHQYDVR0OBBYEFKE0+A+tQNYWGRff +dPhuISf1W7qvMB8GA1UdIwQYMBaAFKE0+A+tQNYWGRffdPhuISf1W7qvMA8GA1Ud +EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD0uiU+vjpLIRqshFBtzWJ7K +zRXb6zR1EGhbs63HRoTmW4s/YCuwZqdvmggd4j5a9RtFCIRO/4MjswrXV6rG0IiN +JXSb4ugz44oGUXsWboGHq3e8/pTJMq+cT4EO2t+irZ2qPe+jtZOJQwDU2NEJzkN6 +P+mcNR1I3pVfA+h1kLDspyv/6pbGQrfo0S8sJzetGYSFPBvEOagS6iHRlC7PwqrZ +gCADDHeewNksX8wGqiselXYQbiIrcGrb42fDgoX0aovEqUuux8Ukifm+DU1P9Hzv +FdMuT/pFNcwOXaETKN4oS4zMB/qGxqEnh3d7oItSBY3XjTgSORC25yf3irTWTww= +-----END CERTIFICATE----- diff --git a/LDN10/certs/janez_private.key b/LDN10/certs/janez_private.key new file mode 100644 index 0000000..6b74fd1 --- /dev/null +++ b/LDN10/certs/janez_private.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXfRqIwv0mhAFB +aKWNU+j/1No2ZQZGJRoC4eEFizncEK9zvUsGyqL1WggDbRVgaL1xWnbidndfUWhC +PAm334zjZWCBnnKaaHil6Envc+6ko0D76p4o3DlS8rizbEzNVJnsLHQ41cWvmYyz +QJKlN0KypdvJrPeOZ6IGGLzmIsjXpcG+lZAovkpvhru5Q0MrWTrXhQPaNzi1z+bW +zyU9wI0W10XrgERxct0Oq27q7M8lH3k/V2rUEPcy1gJZ1N8N12ubz+5SRy6BdTYl +XW5hNh9diz0eNyJmtMy3wzn/GKWNg0GhDZTbjLieDUY1MnSOvZG6JwGmTV9fehZl +9Nxh2VAlAgMBAAECggEADrICoHC16QYWLxetM6QHrsWhs3QiYyyXNN9fmk+Suc/e +jU44GH0QyNdGfrqWv8b5VaUucjKGyvbVM9OqJ62md6qubXSG7oai6Yk6nuFyrYLu +u2FbnWAbqXnFRBaF7sc9tUTss1S9fBbbqMnjT+an8DeZthsy2pwgAhJlRLfsHssd +2nbXhF2Ko9lMHECzqPggD0LKN7UfDTOqdJHiSm1svMiY026ec1VUe0UepC7F1o2V +mG39RPqFjTnsXJ2ajzFDVMvWM75J8DfTvsdAyaxjvoWamMrZap8ibWpnm0UxSWIh +A46O5y4/CSxUVl3Bp/Oxnu+Agdo5fHA8yqlilC5a8wKBgQDPDajNhaC3YnvBiftg +ImKqbjbprPppbjs6IQfNw4tMoKgQ6zPOCAm4fZCBs1ag4VwXmd6HH12f7dsgYIIV +cbz98MoH7RUwGAPV33QxbImp00LhhjHNE70ZeGz399+XXuRnER5MDoII1jcQCGTQ +qf15RgtjHMNC8S6kPhQLTNktMwKBgQC7TM/t+gfE94CZme/D4ZxDZ488UtVppt1k +KKsE6qqLSkyy0MPmf3xwOakS+cy+AZY8Votx2eAyk2wCq2MhCdvz21ZQZWB5pmWj +vKO74a4xpyPuX2gzlMhX/9y1usNfIvyUjMVTzfEWhrCFWlywctcGXkcruHthM3R9 +QOWzFIUdRwKBgGfEs73U47gkrltP2ODUOtiphfHU1Gc0bJB0b2HLR6vutGxORFdU +pfKFU3/9LkRQzYOyhNj5ekWlwnVWiLtQlmQ6YjZYto606m8snyIKAHBRRaqbQ6hA +5Tu3o/0eiDtnvlZ9OM7rUnY1TcFGQ6hkGXnHBfv1NVL+Zt6/kfIb4JgLAoGBAIZs +M1LNPmQ7aX1TwINp2Rd7LI6TuHyx+HRYx5youqtrMK6pK9+yoWF90H1A8JNxWhyF +1GBqiQBjU4J69JMSm1xhj4n1qA7GwG2KBkziRH14Qk5jBMr4Pe0CQanflzYhSvtm +KHzj8eJD2k5qstkkInR8l0Gmjk4Nes4mFHS5BmwjAoGAaHU+iAIKKZwIl4ta6ih7 +NpKH/oC+dGI8I7EoCwt7uk1y6+k7v3uTWZHS6TimVRaiRnRp21EjhC4WdJ4uKoia +wynq8v3palgKbMPvocp/Ni4XYjqFL9UG7OFY1Bl7N90XyuxGNJNzL3ZCe4L5Cip7 ++cuWKDY8V09Jml8wMEyu1FQ= +-----END PRIVATE KEY----- diff --git a/LDN10/certs/server_cert.crt b/LDN10/certs/server_cert.crt new file mode 100644 index 0000000..ac10eef --- /dev/null +++ b/LDN10/certs/server_cert.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIUEZiP8qpFxBwq/1urH/vY8xLPa30wDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X +DTI0MDUyNjE1NTcxOFoXDTI1MDUyNjE1NTcxOFowWTELMAkGA1UEBhMCQVUxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxoKRGOyYRuDCMyd/Q3IHEQXRdJPg/hE5C1yzJGrLyoMjPhLLLFww +GobQE4xCXA/7bzUpVyOHy+1t80YlD4GIiWz+x04U6thsTTh9oMCIK2Uo3lSzyS2W +JINzbT1dQuvt4icgEzcyDYUA6P7rFe+iMFKPxw0h7kPHPtnnpuFsq0SLk7Aopj6a +7TpvxtjSpvcc/D7MLAqAA6u1Ws/JOmdN+4ZD2W4P/t4yiUCzyESxjPYy1DbgLpEr +w82wVcXjBgV82MmtmXZTgcPVNHM1FFXURPCkwOmwjatyRu+S8feBCXJnEfMYrmJB +Kk5AKXkb3viAxAoYokx388tv2YgpknDtCwIDAQABo1MwUTAdBgNVHQ4EFgQUMem3 +SX7Wo2R9gX+BzVQ5rznFsZ0wHwYDVR0jBBgwFoAUMem3SX7Wo2R9gX+BzVQ5rznF +sZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEARAsNxVNw5Tsz +6Kh5nm3DhCVbaXjy2jdnFK/Arleay2VXQQaVHf8+9USz6hxmo2qKbf6noY5sXIrO +TAb/Eig2XXEYJth5qIHpNHBY7ccRhrAsMokbSQSlUUz7Ip10AoVDVqwp9P6GWdei +Er2Hgw9JyzaKcbo2rMR/r5TGYnJ4Yg2f0Z5aO9uBCUdqTZvJfHulVEgn+YsCRV90 +Tz4TVleel3vS6Jljqtj6Rie8DnIX9q1bQQjEten6wHKy2eglpp2uJTPSVThxDTrA +7PQBuizGsvPohjBgNx92oOSXIeeJHHPViKb4wTDrTnNPdkK7pKk7uJMGkh6SwhSf +bYnwL9cS+A== +-----END CERTIFICATE----- diff --git a/LDN10/certs/server_private.key b/LDN10/certs/server_private.key new file mode 100644 index 0000000..7895d09 --- /dev/null +++ b/LDN10/certs/server_private.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDGgpEY7JhG4MIz +J39DcgcRBdF0k+D+ETkLXLMkasvKgyM+EsssXDAahtATjEJcD/tvNSlXI4fL7W3z +RiUPgYiJbP7HThTq2GxNOH2gwIgrZSjeVLPJLZYkg3NtPV1C6+3iJyATNzINhQDo +/usV76IwUo/HDSHuQ8c+2eem4WyrRIuTsCimPprtOm/G2NKm9xz8PswsCoADq7Va +z8k6Z037hkPZbg/+3jKJQLPIRLGM9jLUNuAukSvDzbBVxeMGBXzYya2ZdlOBw9U0 +czUUVdRE8KTA6bCNq3JG75Lx94EJcmcR8xiuYkEqTkApeRve+IDEChiiTHfzy2/Z +iCmScO0LAgMBAAECggEABdfH/4zPOp7Y+Hp8I3cSwCyEqsLDh5boL+5tk2/VScOs +YOJaupaID+/R5hFlzqHly7Mr4VW5ZIY7i0KQSrGMXEcqBKHGudbe8TLtd7LTDzvu +BQVHOW+vEKoR1rBMs3CQUuzF4+rm/UDh8vm137Jq+rMjaj93tTSng7EUQXQ3J59S +mgCm48GIuZmTOKd14KrsmKjBUbB15XNqoKsIqIrIBJ3CUvPnmpneztD8jxAd0zm4 +wk9XJ63MZduZmjpNsJ82m1VXPSKHPt2euKYEQTZlNs2dsnrcSYATJKMCgY8ImDpb +LpjnUM1wiIO33b5YsinLFO0Vlxk3ph/VaWa29fK7MQKBgQDnRZTL3fxa8exMIDSK +Jil+BszzikCPdEvNfHtUyC/dR2Jn4MPE3hwGB2rcWmSrj3wPfIoU9gYj8n2rt+gu +HJ3+p6saRE36n1ejZ43hRgbdb+9aVTZT616lFKz3F7PHyASa8pMIZ9O2924Mq+jt +S9HqZvySCE50FORuZvSNYzdk2QKBgQDbvDk8nKxa533Oa36nxdbgLN8SCXcu0Dqa +fJEPgllXCaK9OHgOA3/Dkvt7Ui54eX1TPz4vBGTMrI1FASWDr44U4Rn8xmQmROmH +XQ4VIsJdyiUwZYWbfcGigGglqgTMVM+vEKIIWhkLuyMJfhDagD2lh6GQIeoCXZPb +dgKkMR+igwKBgQCyIICbiQ2uSnkSpRdydDQvEjPXPFu/YfZkfYVMZqefCjU8Bv0h +62SwLBRdRe4VyXznPKfpvxg5pbKviQV2RqpWEnQR1hwtdX79p2u638G32lgDPnCK +sFWEVmlmI0ZgEKmjYkRnC/kgCnEjp2DPLD+xB8NvAqDSfaj3c6rWH13x+QKBgQCW +nqfxR0fRrBqNcxvHbxKCad+iooBiw3NYUAizQ9tvkPhPkjShDn72lXrypnuaM1TX +ymMaoX1i2uifI1EOuji/USmaHnepz9Tv57ZugHwRBC6HxR9XXVVsyW4aWzecxInz +64fWB8RhSS6UJCjzTbJ0E8uIW+bnJjPi3DqlR9LydwKBgQDNKAJeYBuiMQaBpLcj +YA44tTmqtydZbnjxhZ4bnn7/4N9uCKTSmLCDo/J+CbPT9HGImlmx+s72CREZBSAP +doUYM1LT9Iwj8elEmACTaLkcrkM4+qF4wLeUicYxXRcMmyhc38re85PYqfWCdqXs +dKydz9baCWldimI7/soS/fGUVw== +-----END PRIVATE KEY----- diff --git a/LDN10/client.py b/LDN10/client.py new file mode 100644 index 0000000..5374781 --- /dev/null +++ b/LDN10/client.py @@ -0,0 +1,107 @@ +import json +import socket +import struct +import sys +import ssl +import threading +from datetime import datetime + +PORT = 1234 +HEADER_LENGTH = 2 + + +def setup_SSL_context(): + #uporabi samo TLS, ne SSL + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + # certifikat je obvezen + context.verify_mode = ssl.CERT_REQUIRED + #nalozi svoje certifikate + context.load_cert_chain(certfile="certs/janez_cert.crt", keyfile="certs/janez_private.key") + # nalozi certifikate CAjev (samopodp. cert.= svoja CA!) + context.load_verify_locations('certs/server_cert.crt') + # nastavi SSL CipherSuites (nacin kriptiranja) + context.set_ciphers('ECDHE-RSA-AES128-GCM-SHA256') + return context + + +def receive_fixed_length_msg(sock, msglen): + message = b'' + while len(message) < msglen: + chunk = sock.recv(msglen - len(message)) # preberi nekaj bajtov + if chunk == b'': + raise RuntimeError("socket connection broken") + message = message + chunk # pripni prebrane bajte sporocilu + + return message + + +def receive_json(sock): + # preberi glavo sporocila (v prvih 2 bytih je dolzina sporocila) + header = receive_fixed_length_msg(sock, HEADER_LENGTH) + message_length = struct.unpack("!H", header)[0] # pretvori dolzino sporocila v int + + message = None + if message_length > 0: # ce je vse OK + message = receive_fixed_length_msg(sock, message_length) # preberi sporocilo + message = message.decode("utf-8") + message = json.loads(message) + + return message + + +def send_json(sock, data): + data["time"] = datetime.now().strftime("%c") + + # pretvori sporocilo v niz bajtov, uporabi UTF-8 kodno tabelo + encoded_data = json.dumps(data).encode("utf-8") + + # ustvari glavo v prvih 2 bytih je dolzina sporocila (HEADER_LENGTH) + # metoda pack "!H" : !=network byte order, H=unsigned short + header = struct.pack("!H", len(encoded_data)) + + data = header + encoded_data # najprj posljemo dolzino sporocilo, sele nato sporocilo samo + sock.sendall(data) + + +def send_message(sock, message): + send_json(sock, { + "type": "message", + "data": message, + }) + + +# message_receiver funkcija tece v loceni niti +def message_receiver(): + while True: + msg_received = receive_json(sock) + + msg_type = msg_received["type"] + data = msg_received["data"] + time = msg_received["time"] + match msg_type: + case "message": + username = msg_received["username"] + print(f'[{time}] [{username}] : {data}') + case "error": + print(f'Error: {data}') + + +# povezi se na streznik +print("[system] connecting to chat server ...") +my_ssl_ctx = setup_SSL_context() +sock = my_ssl_ctx.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) +sock.connect(("localhost", PORT)) +print("[system] connected!") + +# zazeni message_receiver funkcijo v loceni niti +thread = threading.Thread(target=message_receiver) +thread.daemon = True +thread.start() + +# pocakaj da uporabnik nekaj natipka in poslji na streznik +while True: + try: + msg_send = input("") + send_message(sock, msg_send) + except KeyboardInterrupt: + sys.exit() diff --git a/LDN10/server.py b/LDN10/server.py new file mode 100644 index 0000000..daf46de --- /dev/null +++ b/LDN10/server.py @@ -0,0 +1,165 @@ +import json +import signal +import socket +import struct +import ssl +import threading +from datetime import datetime + +signal.signal(signal.SIGINT, signal.SIG_DFL) + +PORT = 1234 +HEADER_LENGTH = 2 + +def setup_SSL_context(): + #uporabi samo TLS, ne SSL + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + # certifikat je obvezen + context.verify_mode = ssl.CERT_REQUIRED + #nalozi svoje certifikate + context.load_cert_chain(certfile="certs/server_cert.crt", keyfile="certs/server_private.key") + # nalozi certifikate CAjev, ki jim zaupas + # (samopodp. cert. = svoja CA!) + context.load_verify_locations('certs/clients.pem') + # nastavi SSL CipherSuites (nacin kriptiranja) + context.set_ciphers('ECDHE-RSA-AES128-GCM-SHA256') + return context + +def receive_fixed_length_msg(sock, msglen): + message = b'' + while len(message) < msglen: + chunk = sock.recv(msglen - len(message)) # preberi nekaj bajtov + if chunk == b'': + raise RuntimeError("socket connection broken") + message = message + chunk # pripni prebrane bajte sporocilu + + return message + + +def receive_json(sock) -> dict: + # preberi glavo sporocila (v prvih 2 bytih je dolzina sporocila) + header = receive_fixed_length_msg(sock, HEADER_LENGTH) + message_length = struct.unpack("!H", header)[0] # pretvori dolzino sporocila v int + + message = None + if message_length > 0: # ce je vse OK + message = receive_fixed_length_msg(sock, message_length) # preberi sporocilo + message = message.decode("utf-8") + message = json.loads(message) + + return message + + +def send_json(sock, message): + message["time"] = datetime.now().strftime("%c") + encoded_message = json.dumps(message).encode("utf-8") # pretvori sporocilo v niz bajtov, uporabi UTF-8 kodno tabelo + + # ustvari glavo v prvih 2 bytih je dolzina sporocila (HEADER_LENGTH) + # metoda pack "!H" : !=network byte order, H=unsigned short + header = struct.pack("!H", len(encoded_message)) + + message = header + encoded_message # najprj posljemo dolzino sporocilo, slee nato sporocilo samo + sock.sendall(message) + + +def send_error(sock, message): + send_json(sock, { + "type": "error", + "data": message, + }) + + +# funkcija za komunikacijo z odjemalcem (tece v loceni niti za vsakega odjemalca) +def client_thread(client_sock, client_addr): + global clients + global usernames + + cert = client_sock.getpeercert() + for sub in cert['subject']: + for key, value in sub: + if key == 'commonName': + usernames[client_sock] = value + + print("[system] connected with " + client_addr[0] + ":" + str(client_addr[1])) + print("[system] we now have " + str(len(clients)) + " clients") + + try: + + while True: # neskoncna zanka + msg_received = receive_json(client_sock) + msg_type = str(msg_received["type"]) + data = str(msg_received["data"]) + time = str(msg_received["time"]) + + if not msg_received: # ce obstaja sporocilo + break + + match msg_type: + case "message": + username = usernames[client_sock] + if username is None: + username = client_addr[0] + ":" + str(client_addr[1]) + msg_received["username"] = username + + print(f'[{time}] [{username}] : {data}') + + if data.startswith("@"): + [username, *data] = data.split() + username = str(username).replace("@", "") + + cl = [c for c, u in usernames.items() if u == username] + if len(cl) == 0: + send_error(client_sock, "Client with this username does not exist.") + continue + client = cl[0] + + msg_received["data"] = "".join(data) + send_json(client, msg_received) + continue + + for client in clients: + if client == client_sock: + continue + send_json(client, msg_received) + + except: + # tule bi lahko bolj elegantno reagirali, npr. na posamezne izjeme. Trenutno kar pozremo izjemo + pass + + # prisli smo iz neskoncne zanke + with clients_lock: + clients.remove(client_sock) + if client_sock in usernames: + usernames.pop(client_sock) + + print("[system] we now have " + str(len(clients)) + " clients") + client_sock.close() + + +# kreiraj socket +my_ssl_ctx = setup_SSL_context() +server_socket = my_ssl_ctx.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) +server_socket.bind(("localhost", PORT)) +server_socket.listen(1) + +# cakaj na nove odjemalce +print("[system] listening ...") +clients = set() +usernames = dict() +clients_lock = threading.Lock() +while True: + try: + # pocakaj na novo povezavo - blokirajoc klic + client_sock, client_addr = server_socket.accept() + with clients_lock: + clients.add(client_sock) + + thread = threading.Thread(target=client_thread, args=(client_sock, client_addr)) + thread.daemon = True + thread.start() + + except KeyboardInterrupt: + break + +print("[system] closing server socket ...") +server_socket.close() diff --git a/LDN9/dns-transfer.pcapng b/LDN9/dns-transfer.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..590cb6764f70cd796c84fd3bc83edd6f14d9b419 GIT binary patch literal 2204 zcmb_dOH30{6umPoLunupOcagM0jstG4O6BFgeVx$#7NZC#6?$yR$KYi@-YgUCKAI& z2@55dNV+l6#Eqb0L`mJaFfLs9S-2E7CL%FF0E2k%8`^1SDr({*mwE5yy?gFC_e{r} zlVgqoKx0kub`GCw>w||aP!|fd`o%^`v|R{x1w^;IL3TLp00!6#ZQkbNZc&mQt`f1S zJJ{+Io#lu3SBbWIZ}>v69aj#Qqs$IQa6)}|Td3I^5^bl0VSi`98*Z_Sn8_iFwjTB@ zEh{}Mo$DCO{dnMYij^J?v&y>3!ef$v;UzMJkD~_y<@b%@g`q@ z0|kO-AI>Qh`X83=>kI{9RQqRVU$CgYto|TQf^&(Z8eu&OyuH~ zHDyT78qd|w@+)=Kh=Rw9YYOGodR@=};I1wLsLg$0rK%{~=K*lR!SCm1M)5ewhW|Oq zAXQWvq$+1-u)Wml^M#Gxa0v4uGXT4Zox&bvu?b-lpLfbO)248j;2Fm?MG7CP;jTYQ zH4L31e5wOnSjA9w-^^%#1QFQZMm5B;StVK35s(e-os!wt(G>3J>1aLI%!gaR9O&rm zYV&q>`NOeFEWiMCk~ld^@bp-E|A!wJ6$;im~7LNg#kdcucA}++Km9}wEzpq&Z z&rzIH=ya{IJgkLUVR$00>rfrIu5R9TgKkIpl{ujfU1-dBWaLfe?(3HKw9gp~nF@v$ zOWv(w>GZ2X^$=?!pz1eNm-nit9PtLDc|QkEd{wcoyuAO7@KUjS1doGw#SrTd&M9=f zWXl-iHKpM-hg9c#~?GZc=a89Aa7`2Qu40l?Ld~UHx z(qIIpXE71E72~%~`2pNNI)&Fdg*XtGWW8=9m$+WK-pC_Qh|A7rn%>CRn`P8%wfB?4 zCEgcRFDvoEL3+Km2_7ET6grIgkqN@sv78nopIgn$OJK|~uT4$Q_D4{w49fo>_4>n` z#Yw1tw)&=V&ZM<~Z>}>|nbe{Dm>fHT1?sW_kT%*rl-t!l=sYD`9x+cn*LZ3feLIhR zSm-TOG(vqoU@zv>J|y?rQM@6>V}>!A79$ri#J|CqO2W|fS{`}I{Pa}Q>v{CkN+cKk zv{k_%4#Xur52<8B9?Y}DL9G^;Rs{(jA5*nDMux&cp1pk2o%kN6@JM_Q fl`atzfP{2+qjZ;acRgqBy`S&B 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 G@qYl|5z3JO literal 0 HcmV?d00001