mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
parent
1ba6e49e5d
commit
c9aae81eae
20 changed files with 1631 additions and 373 deletions
23
certificate.pem
Normal file
23
certificate.pem
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAjegAwIBAgIUYMCyNaykR4OF8u4Bq+pesqG48LQwDQYJKoZIhvcNAQEL
|
||||
BQAwADAgFw0yNjA1MjAxOTA0NDFaGA85OTk5MTIzMTIzNTk1OVowADCCAaIwDQYJ
|
||||
KoZIhvcNAQEBBQADggGPADCCAYoCggGBAL5uhNcLTOs4Senn8zuwy8c15HGDCBk/
|
||||
tSgUFCSueBRtbZuAnPtxlkOkJDB7y/Abp+rhHe7T7sa1iYS+Q7aWK0QIaoiKJj1x
|
||||
35SsCYmniAs797tsxnzfRuP2WJe8yt/kuia6d6AL12P5Yu05JEJWaiDHljfSZsGB
|
||||
spCD5c2PyYZqGKNxdUHLlFDZ3RIWkym8h/wTKESlIfKcO0l9Ythtz0qAFWZbOJRR
|
||||
VXUIJ6djYtc3C3cIIa6P18598CkRlmk7WSRfSrUY1USrzCL5dwBnfqLNqyWuqHWk
|
||||
hJLM7Izu2ZR24V4rQpf7V32jAUkWCwRUyseqkilgYIV3MWLoLlBP5jLTRBZowIJ2
|
||||
h6EnqGNfUqKuR9W/sT21lkP+XUJPNfPEjL5paG1jpyMCwvWHrfVd63wjDUaZxa52
|
||||
APZlhDLoKPrMI4jBuV0yBnO8m2PKVHGbfytuCUSWRW/+YHtoIBVJ5GpPbZ7XRjan
|
||||
/p/Rk++XVXlKyo9b66PDQ/qBEzTrsFie2QIDAQABoz8wPTAMBgNVHRMBAf8EAjAA
|
||||
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHQ4EFgQUSCH7PDVIF0erciboK/dzV1i+hC8w
|
||||
DQYJKoZIhvcNAQELBQADggGBACVS1GPwXvHxs5PFV8dobDXDbzIF984YJGkfkpTI
|
||||
eQiC2GdQZd+d2NQdYJYu+yFQrmfPyhH4FPH6uLUp2eNGexXlv58TNMkh69MekapJ
|
||||
+XBBwwCynL4vZETtozE6Dzp5S7XXL3cenYI+0MIforEhI3dtbjg2zxRQy4YiBYuh
|
||||
tecbazuNsVXw7iSN8yT0wZTfg4gGHDyzEdO4+8BcBUfzpBDOr+/TV58doIA/ZucX
|
||||
utFR8o9tVmvVEef8LaEOzmn7fgpxaEiVEbFH1PUyRhMZlQRN/eTTkDTRiTzssoXA
|
||||
dtIV5M1kJjkdn/aYxM8h+unJ6/MfPvuat+HcmKDXVhdoNJ2r23mUwRFPt7fwm75Y
|
||||
gp8WWwP9D8MglcVHhsp+KrDKfFX7MicDPhadc6LEWveo4PRRdJcKcWedJEvnWFNN
|
||||
0TbbC5xQgIB1KijxvqoBr38XQVhFry1dZRPBv642YBMdiHWGCoLE8EBssDqs5Wp+
|
||||
DMqs4E5nZzbVVSM5l5J2q6Wlbw==
|
||||
-----END CERTIFICATE-----
|
||||
182
privatekey.pem
Normal file
182
privatekey.pem
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
Public Key Info:
|
||||
Public Key Algorithm: RSA
|
||||
Key Security Level: High (3072 bits)
|
||||
|
||||
modulus:
|
||||
00:be:6e:84:d7:0b:4c:eb:38:49:e9:e7:f3:3b:b0:cb
|
||||
c7:35:e4:71:83:08:19:3f:b5:28:14:14:24:ae:78:14
|
||||
6d:6d:9b:80:9c:fb:71:96:43:a4:24:30:7b:cb:f0:1b
|
||||
a7:ea:e1:1d:ee:d3:ee:c6:b5:89:84:be:43:b6:96:2b
|
||||
44:08:6a:88:8a:26:3d:71:df:94:ac:09:89:a7:88:0b
|
||||
3b:f7:bb:6c:c6:7c:df:46:e3:f6:58:97:bc:ca:df:e4
|
||||
ba:26:ba:77:a0:0b:d7:63:f9:62:ed:39:24:42:56:6a
|
||||
20:c7:96:37:d2:66:c1:81:b2:90:83:e5:cd:8f:c9:86
|
||||
6a:18:a3:71:75:41:cb:94:50:d9:dd:12:16:93:29:bc
|
||||
87:fc:13:28:44:a5:21:f2:9c:3b:49:7d:62:d8:6d:cf
|
||||
4a:80:15:66:5b:38:94:51:55:75:08:27:a7:63:62:d7
|
||||
37:0b:77:08:21:ae:8f:d7:ce:7d:f0:29:11:96:69:3b
|
||||
59:24:5f:4a:b5:18:d5:44:ab:cc:22:f9:77:00:67:7e
|
||||
a2:cd:ab:25:ae:a8:75:a4:84:92:cc:ec:8c:ee:d9:94
|
||||
76:e1:5e:2b:42:97:fb:57:7d:a3:01:49:16:0b:04:54
|
||||
ca:c7:aa:92:29:60:60:85:77:31:62:e8:2e:50:4f:e6
|
||||
32:d3:44:16:68:c0:82:76:87:a1:27:a8:63:5f:52:a2
|
||||
ae:47:d5:bf:b1:3d:b5:96:43:fe:5d:42:4f:35:f3:c4
|
||||
8c:be:69:68:6d:63:a7:23:02:c2:f5:87:ad:f5:5d:eb
|
||||
7c:23:0d:46:99:c5:ae:76:00:f6:65:84:32:e8:28:fa
|
||||
cc:23:88:c1:b9:5d:32:06:73:bc:9b:63:ca:54:71:9b
|
||||
7f:2b:6e:09:44:96:45:6f:fe:60:7b:68:20:15:49:e4
|
||||
6a:4f:6d:9e:d7:46:36:a7:fe:9f:d1:93:ef:97:55:79
|
||||
4a:ca:8f:5b:eb:a3:c3:43:fa:81:13:34:eb:b0:58:9e
|
||||
d9:
|
||||
|
||||
public exponent:
|
||||
01:00:01:
|
||||
|
||||
private exponent:
|
||||
54:d4:52:a4:a0:ca:10:f6:30:26:dc:46:83:ce:8b:d8
|
||||
1f:ef:b5:89:13:30:7c:2a:ac:c1:d4:ff:4a:20:ff:a8
|
||||
87:6c:ff:eb:ee:2e:79:2b:84:91:02:70:03:36:e0:7a
|
||||
fa:ac:71:73:14:41:87:8f:12:c5:69:24:2c:cf:d4:52
|
||||
28:15:9c:e1:3d:8b:9d:90:65:60:05:97:a6:63:79:ed
|
||||
aa:bb:79:07:2a:55:23:f5:24:a5:ee:62:11:55:8f:44
|
||||
45:40:47:4d:aa:38:b6:b6:3f:15:41:a1:1f:53:f3:4e
|
||||
ca:d9:e5:df:fa:1a:35:36:60:1e:01:5e:82:b0:d9:09
|
||||
a6:14:18:d4:8d:0c:ac:f3:1f:39:d5:76:ec:f3:68:a2
|
||||
82:ed:dd:c0:46:77:4a:e5:c1:9b:49:19:a1:23:b9:75
|
||||
8c:7c:fb:ed:a2:d4:9f:2e:9c:45:97:b4:7f:17:66:9b
|
||||
84:a5:ef:9f:61:6d:7e:4a:e3:da:f9:d0:75:da:46:ae
|
||||
f6:e9:ae:cd:66:11:11:62:5a:3e:22:55:9f:ac:2c:bd
|
||||
a8:5a:62:1e:fe:c6:78:04:1d:69:f3:12:53:c7:5a:db
|
||||
c3:19:a4:d9:d6:5a:25:55:f7:ba:30:d1:f4:bb:8a:b6
|
||||
8a:c5:1d:4c:66:c3:e6:c1:05:4a:d8:9b:2e:f3:b9:3f
|
||||
8a:b6:6e:04:cb:5f:32:65:a0:1c:32:5a:f5:b0:36:a3
|
||||
ed:96:1b:cf:b0:1d:32:3e:8b:a4:53:b2:c0:6d:30:d3
|
||||
93:43:d6:fe:29:fd:9e:e2:72:30:7a:01:2b:b2:ad:39
|
||||
b5:6c:8b:41:67:77:bb:09:8e:41:41:e2:a9:5a:76:4b
|
||||
51:56:43:72:db:02:5e:d9:ef:93:16:c7:64:18:3a:f7
|
||||
73:d8:b8:d0:8f:af:e3:cd:06:26:8f:af:b0:b6:57:f6
|
||||
f0:c7:4f:f0:6f:f5:49:07:96:83:ae:44:60:5b:1f:df
|
||||
0f:70:07:c4:06:03:bf:47:f3:e8:a8:1e:54:ba:46:e9
|
||||
|
||||
|
||||
prime1:
|
||||
00:f8:6f:14:90:ad:01:c6:ea:8e:e0:ad:bd:27:fd:af
|
||||
e5:09:6b:24:02:c5:18:aa:69:d1:d4:0f:78:06:89:c1
|
||||
1e:5f:5a:3c:21:ca:1e:a3:73:0d:5e:d6:42:e9:6c:b0
|
||||
81:85:85:b7:d7:57:b2:27:42:67:5b:06:fa:e7:32:64
|
||||
bd:f5:fe:31:44:fb:a2:8e:b9:e8:3a:da:42:1a:13:20
|
||||
47:ac:8e:e1:f0:7e:81:1d:c9:de:ae:81:f5:fc:07:be
|
||||
4d:df:a1:a4:36:bb:c8:b4:f5:96:bb:bc:cf:b8:53:28
|
||||
15:28:32:f8:82:0d:9e:ed:0c:75:d5:83:a6:9f:cd:d1
|
||||
37:42:ad:6d:a4:75:0c:e6:9e:dd:16:6d:f9:57:56:9a
|
||||
0c:9c:f8:90:86:f8:a9:92:c8:61:81:09:82:a8:89:a9
|
||||
8f:c6:71:77:63:40:77:46:ed:c8:78:41:7b:ac:95:c2
|
||||
23:b2:64:44:26:00:d9:6b:b1:a8:99:28:78:7e:71:ed
|
||||
a3:
|
||||
|
||||
prime2:
|
||||
00:c4:3b:39:2f:33:08:d4:22:43:0f:4a:69:a9:89:51
|
||||
c9:1a:9f:a2:e2:82:bb:fd:77:76:c0:5a:36:38:d0:36
|
||||
ee:f2:aa:8b:4a:2a:56:df:eb:88:7f:50:bf:55:bc:9d
|
||||
cb:e2:83:58:9d:60:3e:b5:6e:6f:89:61:4c:09:a1:99
|
||||
59:78:77:38:fe:b7:87:45:6e:54:5d:52:ef:18:25:e2
|
||||
39:f4:04:9d:0e:bd:05:c3:a1:06:11:d8:93:8e:59:85
|
||||
0e:18:bf:80:fd:aa:a8:c6:20:3e:98:ea:82:6e:2f:37
|
||||
64:ec:19:e8:ab:f5:c8:b1:f6:ff:af:ef:f5:37:e0:d2
|
||||
4c:2f:eb:ca:22:fc:56:7e:9e:f5:41:df:f5:c2:06:17
|
||||
5a:e4:0a:c3:fb:fe:4f:00:0c:f5:36:8b:1b:33:e9:11
|
||||
d2:44:ac:3e:d0:d9:e7:c7:17:e5:3a:4e:ed:5c:73:7e
|
||||
73:83:4c:28:a0:1b:78:7c:82:f9:20:7b:8b:29:68:51
|
||||
53:
|
||||
|
||||
coefficient:
|
||||
00:aa:69:84:80:d9:0d:3a:12:90:d8:4e:cf:9c:24:5a
|
||||
5b:40:c0:41:b4:73:db:51:4a:7a:3b:91:9c:51:01:6c
|
||||
41:de:a3:33:e9:2e:ef:3b:2c:42:fd:75:98:f4:47:f0
|
||||
b3:12:f6:d6:ff:88:3e:d2:68:b7:ed:10:6e:38:c6:f4
|
||||
be:0a:82:17:b3:62:b5:25:d2:c2:54:b9:5b:e8:a1:6a
|
||||
04:2d:6a:bb:73:b7:9d:5f:d2:7f:5d:7c:c0:50:4a:8e
|
||||
af:e9:a8:68:b3:f7:a3:d9:71:0c:84:e4:c8:68:0b:10
|
||||
5a:71:f0:f5:92:ee:20:2f:0c:a5:24:6b:1a:7e:c0:3c
|
||||
28:4a:37:fd:ad:2f:fa:15:87:97:a1:d0:b1:62:26:f3
|
||||
96:a2:33:8d:41:d8:6a:0b:ce:c9:bf:c2:0d:8e:94:84
|
||||
66:27:a4:c1:00:32:64:ad:8a:3e:45:45:4b:c1:d1:c4
|
||||
7d:f2:d1:3a:f6:65:36:46:2e:52:35:b9:a2:ab:87:3c
|
||||
66:
|
||||
|
||||
exp1:
|
||||
00:8c:40:f1:e4:47:b7:f4:1b:e1:f3:d8:42:2c:fc:9c
|
||||
bb:fa:58:41:69:4a:ea:84:f2:de:e2:10:a5:9b:53:53
|
||||
f2:98:b4:71:b4:45:ce:8c:4a:5c:e7:08:a1:97:f4:a3
|
||||
a5:4b:c3:55:29:be:b9:b6:4e:57:d9:5d:14:73:47:d0
|
||||
f6:29:95:8b:2d:3d:be:e5:42:f1:67:a0:66:a7:1f:db
|
||||
1e:7b:bd:e2:b2:8a:48:cc:8f:76:27:20:f9:c5:82:7d
|
||||
9e:ab:3d:2f:5f:33:1e:b9:82:d8:c9:3e:6c:2a:cc:cf
|
||||
99:3a:2b:a4:7e:8f:c0:04:65:ff:74:3e:31:e8:90:22
|
||||
a8:46:fd:70:23:e3:6d:18:19:e2:09:52:a6:ec:f6:d8
|
||||
5b:7c:97:1b:c9:07:43:7c:b9:a1:ca:5e:9b:24:19:2b
|
||||
e0:1e:91:5a:6c:6f:2d:a7:9e:80:89:db:b6:3d:96:02
|
||||
97:72:94:06:a1:49:e3:75:58:44:2d:cb:5a:53:50:70
|
||||
6f:
|
||||
|
||||
exp2:
|
||||
25:93:05:87:21:29:8e:9d:24:e4:17:a6:95:dd:02:79
|
||||
14:8c:fe:be:8a:b3:fe:7b:d2:94:50:71:d3:7d:23:17
|
||||
ac:05:b5:f8:34:95:3f:f9:34:c0:d4:30:5e:f5:67:ed
|
||||
b3:68:dd:1d:fd:60:e4:92:c9:ee:af:5f:c4:f4:59:8d
|
||||
c5:40:66:fc:77:1e:02:d0:76:7d:0c:35:56:15:62:f5
|
||||
1f:e1:86:45:5d:32:6e:5c:35:f2:52:db:26:45:c3:f1
|
||||
88:11:9b:5c:77:42:2b:f5:de:a6:9f:38:ec:6a:44:1c
|
||||
22:0d:6f:fd:05:6a:31:91:8f:32:1c:2b:83:50:9c:54
|
||||
14:54:fc:f6:a8:04:d3:e8:12:24:54:03:15:ec:de:a9
|
||||
fb:c2:87:f6:87:a2:8e:ea:ec:45:4e:6b:9e:0c:01:ea
|
||||
96:55:b9:0d:7a:bc:23:e6:52:71:50:cd:a8:87:40:ee
|
||||
53:74:d0:ce:9f:93:f9:9b:86:a2:8e:a5:7d:ff:48:9b
|
||||
|
||||
|
||||
|
||||
Public Key PIN:
|
||||
pin-sha256:mTYJ3eMVIy4Zcqoo22QDSav9huivaJ8VbglPlInerS8=
|
||||
Public Key ID:
|
||||
sha256:993609dde315232e1972aa28db640349abfd86e8af689f156e094f9489dead2f
|
||||
sha1:4821fb3c35481747ab7226e82bf7735758be842f
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIG5AIBAAKCAYEAvm6E1wtM6zhJ6efzO7DLxzXkcYMIGT+1KBQUJK54FG1tm4Cc
|
||||
+3GWQ6QkMHvL8Bun6uEd7tPuxrWJhL5DtpYrRAhqiIomPXHflKwJiaeICzv3u2zG
|
||||
fN9G4/ZYl7zK3+S6Jrp3oAvXY/li7TkkQlZqIMeWN9JmwYGykIPlzY/JhmoYo3F1
|
||||
QcuUUNndEhaTKbyH/BMoRKUh8pw7SX1i2G3PSoAVZls4lFFVdQgnp2Ni1zcLdwgh
|
||||
ro/Xzn3wKRGWaTtZJF9KtRjVRKvMIvl3AGd+os2rJa6odaSEkszsjO7ZlHbhXitC
|
||||
l/tXfaMBSRYLBFTKx6qSKWBghXcxYuguUE/mMtNEFmjAgnaHoSeoY19Soq5H1b+x
|
||||
PbWWQ/5dQk8188SMvmlobWOnIwLC9Yet9V3rfCMNRpnFrnYA9mWEMugo+swjiMG5
|
||||
XTIGc7ybY8pUcZt/K24JRJZFb/5ge2ggFUnkak9tntdGNqf+n9GT75dVeUrKj1vr
|
||||
o8ND+oETNOuwWJ7ZAgMBAAECggGAVNRSpKDKEPYwJtxGg86L2B/vtYkTMHwqrMHU
|
||||
/0og/6iHbP/r7i55K4SRAnADNuB6+qxxcxRBh48SxWkkLM/UUigVnOE9i52QZWAF
|
||||
l6Zjee2qu3kHKlUj9SSl7mIRVY9ERUBHTao4trY/FUGhH1PzTsrZ5d/6GjU2YB4B
|
||||
XoKw2QmmFBjUjQys8x851Xbs82iigu3dwEZ3SuXBm0kZoSO5dYx8++2i1J8unEWX
|
||||
tH8XZpuEpe+fYW1+SuPa+dB12kau9umuzWYREWJaPiJVn6wsvahaYh7+xngEHWnz
|
||||
ElPHWtvDGaTZ1lolVfe6MNH0u4q2isUdTGbD5sEFStibLvO5P4q2bgTLXzJloBwy
|
||||
WvWwNqPtlhvPsB0yPoukU7LAbTDTk0PW/in9nuJyMHoBK7KtObVsi0Fnd7sJjkFB
|
||||
4qladktRVkNy2wJe2e+TFsdkGDr3c9i40I+v480GJo+vsLZX9vDHT/Bv9UkHloOu
|
||||
RGBbH98PcAfEBgO/R/PoqB5UukbpAoHBAPhvFJCtAcbqjuCtvSf9r+UJayQCxRiq
|
||||
adHUD3gGicEeX1o8Icoeo3MNXtZC6WywgYWFt9dXsidCZ1sG+ucyZL31/jFE+6KO
|
||||
ueg62kIaEyBHrI7h8H6BHcneroH1/Ae+Td+hpDa7yLT1lru8z7hTKBUoMviCDZ7t
|
||||
DHXVg6afzdE3Qq1tpHUM5p7dFm35V1aaDJz4kIb4qZLIYYEJgqiJqY/GcXdjQHdG
|
||||
7ch4QXuslcIjsmREJgDZa7GomSh4fnHtowKBwQDEOzkvMwjUIkMPSmmpiVHJGp+i
|
||||
4oK7/Xd2wFo2ONA27vKqi0oqVt/riH9Qv1W8ncvig1idYD61bm+JYUwJoZlZeHc4
|
||||
/reHRW5UXVLvGCXiOfQEnQ69BcOhBhHYk45ZhQ4Yv4D9qqjGID6Y6oJuLzdk7Bno
|
||||
q/XIsfb/r+/1N+DSTC/ryiL8Vn6e9UHf9cIGF1rkCsP7/k8ADPU2ixsz6RHSRKw+
|
||||
0NnnxxflOk7tXHN+c4NMKKAbeHyC+SB7iyloUVMCgcEAjEDx5Ee39Bvh89hCLPyc
|
||||
u/pYQWlK6oTy3uIQpZtTU/KYtHG0Rc6MSlznCKGX9KOlS8NVKb65tk5X2V0Uc0fQ
|
||||
9imViy09vuVC8WegZqcf2x57veKyikjMj3YnIPnFgn2eqz0vXzMeuYLYyT5sKszP
|
||||
mTorpH6PwARl/3Q+MeiQIqhG/XAj420YGeIJUqbs9thbfJcbyQdDfLmhyl6bJBkr
|
||||
4B6RWmxvLaeegInbtj2WApdylAahSeN1WEQty1pTUHBvAoHAJZMFhyEpjp0k5Bem
|
||||
ld0CeRSM/r6Ks/570pRQcdN9IxesBbX4NJU/+TTA1DBe9Wfts2jdHf1g5JLJ7q9f
|
||||
xPRZjcVAZvx3HgLQdn0MNVYVYvUf4YZFXTJuXDXyUtsmRcPxiBGbXHdCK/Xepp84
|
||||
7GpEHCINb/0FajGRjzIcK4NQnFQUVPz2qATT6BIkVAMV7N6p+8KH9oeijursRU5r
|
||||
ngwB6pZVuQ16vCPmUnFQzaiHQO5TdNDOn5P5m4aijqV9/0ibAoHBAKpphIDZDToS
|
||||
kNhOz5wkWltAwEG0c9tRSno7kZxRAWxB3qMz6S7vOyxC/XWY9EfwsxL21v+IPtJo
|
||||
t+0QbjjG9L4KghezYrUl0sJUuVvooWoELWq7c7edX9J/XXzAUEqOr+moaLP3o9lx
|
||||
DITkyGgLEFpx8PWS7iAvDKUkaxp+wDwoSjf9rS/6FYeXodCxYibzlqIzjUHYagvO
|
||||
yb/CDY6UhGYnpMEAMmStij5FRUvB0cR98tE69mU2Ri5SNbmiq4c8Zg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
|
@ -72,7 +72,7 @@ CNetClient::CNetClient(CGame* game, std::string serverAddressOrHostname, std::ui
|
|||
CNetClient{PrivateTag{}, game, std::move(serverAddressOrHostname), serverPort, username, hostJID,
|
||||
std::move(hashedPassword), std::move(controllerSecret)}
|
||||
{
|
||||
SetupConnection(nullptr);
|
||||
SetupConnection();
|
||||
}
|
||||
|
||||
CNetClient::CNetClient(PrivateTag, CGame* game, std::string serverAddressOrHostname,
|
||||
|
|
@ -169,10 +169,10 @@ CNetClient::~CNetClient()
|
|||
}
|
||||
|
||||
|
||||
void CNetClient::SetupConnection(ENetHost* enetClient)
|
||||
void CNetClient::SetupConnection()
|
||||
{
|
||||
CNetClientSession* session = new CNetClientSession(*this);
|
||||
bool ok = session->Connect(m_ServerAddressOrHostname, m_ServerPort, enetClient);
|
||||
bool ok = session->Connect(m_ServerAddressOrHostname, m_ServerPort);
|
||||
SetAndOwnSession(session);
|
||||
if (ok)
|
||||
m_PollingThread = std::thread(Threading::HandleExceptions<CNetClientSession::RunNetLoop>::Wrapper, m_Session);
|
||||
|
|
@ -284,7 +284,7 @@ bool CNetClient::TryToConnectWithSTUN(std::string serverAddressOrHostname, std::
|
|||
|
||||
try
|
||||
{
|
||||
g_NetClient->SetupConnection(enetClient);
|
||||
g_NetClient->SetupConnection();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
|
@ -463,6 +463,7 @@ bool CNetClient::SendMessage(const CNetMessage* message)
|
|||
|
||||
void CNetClient::HandleConnect()
|
||||
{
|
||||
LOGMESSAGE("Net client: Connected", m_ServerAddressOrHostname, m_ServerPort);
|
||||
Update((uint)NMT_CONNECT_COMPLETE, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ private:
|
|||
* Set up a connection to the remote networked server.
|
||||
* @return true on success, false on connection failure
|
||||
*/
|
||||
void SetupConnection(ENetHost* enetClient);
|
||||
void SetupConnection();
|
||||
|
||||
/**
|
||||
* Take ownership of a session object, and use it for all network communication.
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@
|
|||
#include "lib/code_generation.h"
|
||||
#include "lib/debug.h"
|
||||
#include "network/NetClient.h"
|
||||
#include "network/NetEnet.h"
|
||||
#include "network/NetMessage.h"
|
||||
#include "network/NetProtocol.h"
|
||||
#include "network/NetStats.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/ProfileViewer.h"
|
||||
|
|
@ -32,43 +32,399 @@
|
|||
|
||||
constexpr int NETCLIENT_POLL_TIMEOUT = 50;
|
||||
|
||||
constexpr int CHANNEL_COUNT = 1;
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <ngtcp2/ngtcp2_crypto.h>
|
||||
#include <ngtcp2/ngtcp2_crypto_gnutls.h>
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
struct CNetClientSession::Quic
|
||||
{
|
||||
AddressStorage localAddress;
|
||||
std::unique_ptr<gnutls_certificate_credentials_st, CredentialsDeleter> credentials;
|
||||
std::unique_ptr<gnutls_session_int, SessionDeleter> session;
|
||||
std::unique_ptr<ngtcp2_conn, ConnectionDeleter> quicConnection;
|
||||
ngtcp2_crypto_conn_ref connectionReference;
|
||||
int fd;
|
||||
|
||||
std::optional<Stream> streams;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct CreateSocketResult
|
||||
{
|
||||
int descriptor;
|
||||
AddressStorage address;
|
||||
};
|
||||
CreateSocketResult CreateSocket(const char* host, const std::uint16_t port)
|
||||
{
|
||||
addrinfo hints{};
|
||||
hints.ai_flags = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
addrinfo* res;
|
||||
const int rv{getaddrinfo(host, fmt::format("{}", port).c_str(), &hints, &res)};
|
||||
if (rv)
|
||||
throw std::runtime_error{fmt::format("getaddrinfo: {}", gai_strerror(rv))};
|
||||
std::unique_ptr<addrinfo, decltype(&freeaddrinfo)> infoList{res, &freeaddrinfo};
|
||||
|
||||
addrinfo* rp;
|
||||
int fd{-1};
|
||||
for (rp = res; rp; rp = rp->ai_next)
|
||||
{
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (fd != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fd == -1)
|
||||
throw std::runtime_error{"unable to create a socket"};
|
||||
|
||||
return CreateSocketResult{
|
||||
.descriptor{fd},
|
||||
.address{.address{*rp->ai_addr}, .length{rp->ai_addrlen}}
|
||||
};
|
||||
}
|
||||
|
||||
AddressStorage ConnectSocket(const int fd, const AddressStorage& remoteAddress)
|
||||
{
|
||||
if (connect(fd, &remoteAddress.address.sa, remoteAddress.length))
|
||||
throw std::runtime_error{fmt::format("connect: {}", strerror(errno))};
|
||||
|
||||
ngtcp2_sockaddr_union localAddress;
|
||||
ngtcp2_socklen localAddressLength{sizeof(localAddress)};
|
||||
if (getsockname(fd, &localAddress.sa, &localAddressLength) == -1)
|
||||
throw std::runtime_error{fmt::format("getsockname: {}", strerror(errno))};
|
||||
|
||||
return {localAddress, localAddressLength};
|
||||
}
|
||||
|
||||
void ClientGnutlsInit(CNetClientSession::Quic* c)
|
||||
{
|
||||
gnutls_certificate_credentials_t tempCred;
|
||||
const int allocRet{gnutls_certificate_allocate_credentials(&tempCred)};
|
||||
if (allocRet)
|
||||
{
|
||||
throw std::runtime_error{fmt::format("cred init failed: {}: {}", allocRet,
|
||||
gnutls_strerror(allocRet))};
|
||||
}
|
||||
c->credentials.reset(tempCred);
|
||||
|
||||
gnutls_session_t tempSession;
|
||||
if (const int initRet{gnutls_init(&tempSession, GNUTLS_CLIENT | GNUTLS_ENABLE_EARLY_DATA |
|
||||
GNUTLS_NO_END_OF_EARLY_DATA)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("gnutls_init: {}", gnutls_strerror(initRet))};
|
||||
}
|
||||
c->session.reset(tempSession);
|
||||
|
||||
if (ngtcp2_crypto_gnutls_configure_client_session(c->session.get()))
|
||||
throw std::runtime_error{"ngtcp2_crypto_gnutls_configure_client_session failed"};
|
||||
|
||||
if (const int priorityRet{gnutls_priority_set_direct(c->session.get(), TLS_PRIORITY, nullptr)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("gnutls_priority_set_direct: {}",
|
||||
gnutls_strerror(priorityRet))};
|
||||
}
|
||||
|
||||
gnutls_session_set_ptr(c->session.get(), &c->connectionReference);
|
||||
|
||||
if (const int setRet{gnutls_credentials_set(c->session.get(), GNUTLS_CRD_CERTIFICATE,
|
||||
c->credentials.get())})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("gnutls_credentials_set: {}", gnutls_strerror(setRet))};
|
||||
}
|
||||
}
|
||||
|
||||
int OpenStream(ngtcp2_conn*, const std::int64_t streamId, void* userData)
|
||||
{
|
||||
CNetClientSession& session{*static_cast<CNetClientSession*>(userData)};
|
||||
|
||||
session.m_Connected = true;
|
||||
session.m_WasConnected = true;
|
||||
session.m_Quic->streams.emplace(streamId);
|
||||
session.m_IncomingMessages.push(CNetClientSession::ConnectionEstablished{});
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OnStreamDataReceive(ngtcp2_conn* conn, const std::uint32_t /*flags*/, const std::int64_t streamId,
|
||||
const std::size_t /*offset*/, const std::uint8_t* data, std::size_t dataSize, void* userData, void*)
|
||||
{
|
||||
auto& session = *static_cast<CNetClientSession*>(userData);
|
||||
auto message = session.m_Quic->streams.value().Receive({data, dataSize});
|
||||
if (message.has_value())
|
||||
session.m_IncomingMessages.push(new std::vector<std::uint8_t>{std::move(message).value()});
|
||||
increaseWindow(conn, streamId, dataSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ClientQuicInit(CNetClientSession& session, const AddressStorage& remote, const AddressStorage& local)
|
||||
{
|
||||
const ngtcp2_path path{
|
||||
.local{
|
||||
.addr{const_cast<ngtcp2_sockaddr*>(&local.address.sa)},
|
||||
.addrlen{local.length},
|
||||
},
|
||||
.remote{
|
||||
.addr{const_cast<ngtcp2_sockaddr*>(&remote.address.sa)},
|
||||
.addrlen{remote.length},
|
||||
}
|
||||
};
|
||||
constexpr ngtcp2_callbacks callbacks{
|
||||
.client_initial{&ngtcp2_crypto_client_initial_cb},
|
||||
.recv_crypto_data{&ngtcp2_crypto_recv_crypto_data_cb},
|
||||
.encrypt{&ngtcp2_crypto_encrypt_cb},
|
||||
.decrypt{&ngtcp2_crypto_decrypt_cb},
|
||||
.hp_mask{&ngtcp2_crypto_hp_mask_cb},
|
||||
.recv_stream_data{&OnStreamDataReceive},
|
||||
.stream_open{&OpenStream},
|
||||
.recv_retry{&ngtcp2_crypto_recv_retry_cb},
|
||||
.rand{&OnRandomRequest},
|
||||
.get_new_connection_id{&OnNewConnectionIdRequest},
|
||||
.update_key{&ngtcp2_crypto_update_key_cb},
|
||||
.delete_crypto_aead_ctx{&ngtcp2_crypto_delete_crypto_aead_ctx_cb},
|
||||
.delete_crypto_cipher_ctx{&ngtcp2_crypto_delete_crypto_cipher_ctx_cb},
|
||||
.get_path_challenge_data{&ngtcp2_crypto_get_path_challenge_data_cb},
|
||||
.version_negotiation{&ngtcp2_crypto_version_negotiation_cb}
|
||||
};
|
||||
ngtcp2_cid dcid;
|
||||
dcid.datalen = NGTCP2_MIN_INITIAL_DCIDLEN;
|
||||
if (gnutls_rnd(GNUTLS_RND_RANDOM, dcid.data, dcid.datalen))
|
||||
throw std::runtime_error{"gnutls_rnd failed"};
|
||||
|
||||
ngtcp2_cid scid;
|
||||
scid.datalen = 8;
|
||||
if (gnutls_rnd(GNUTLS_RND_RANDOM, scid.data, scid.datalen))
|
||||
throw std::runtime_error{"gnutls_rnd failed"};
|
||||
|
||||
ngtcp2_settings settings;
|
||||
ngtcp2_settings_default(&settings);
|
||||
settings.initial_ts = timestamp();
|
||||
|
||||
ngtcp2_transport_params params;
|
||||
ngtcp2_transport_params_default(¶ms);
|
||||
params.initial_max_stream_data_bidi_remote = 128 * KiB;
|
||||
params.initial_max_data = 1 * MiB;
|
||||
params.initial_max_streams_bidi = 1;
|
||||
params.max_udp_payload_size = MAX_UDP_PAYLOAD_SIZE;
|
||||
params.grease_quic_bit = 1;
|
||||
|
||||
ngtcp2_conn* tempConn;
|
||||
if (const int rv{ngtcp2_conn_client_new(&tempConn, &dcid, &scid, &path,
|
||||
NGTCP2_PROTO_VER_V1, &callbacks, &settings, ¶ms, nullptr, &session)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("ngtcp2_conn_client_new: {}", ngtcp2_strerror(rv))};
|
||||
}
|
||||
session.m_Quic->quicConnection.reset(tempConn);
|
||||
|
||||
ngtcp2_conn_set_tls_native_handle(session.m_Quic->quicConnection.get(),
|
||||
session.m_Quic->session.get());
|
||||
}
|
||||
|
||||
void ClientRead(CNetClientSession::Quic* c) {
|
||||
std::array<std::uint8_t, MAX_UDP_PAYLOAD_SIZE> buf;
|
||||
struct sockaddr_storage addr;
|
||||
iovec iov{
|
||||
.iov_base = buf.data(),
|
||||
.iov_len = buf.size(),
|
||||
};
|
||||
msghdr msg{};
|
||||
msg.msg_name = &addr;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
ngtcp2_pkt_info pi{};
|
||||
while (true)
|
||||
{
|
||||
msg.msg_namelen = sizeof(addr);
|
||||
|
||||
const ssize_t nread{recvmsg(c->fd, &msg, MSG_DONTWAIT)};
|
||||
|
||||
if (nread == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
LOGERROR("recvmsg: %s", strerror(errno));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ngtcp2_path path{
|
||||
.local{
|
||||
.addr{&c->localAddress.address.sa},
|
||||
.addrlen{c->localAddress.length}
|
||||
},
|
||||
.remote{
|
||||
.addr{static_cast<ngtcp2_sockaddr*>(msg.msg_name)},
|
||||
.addrlen{msg.msg_namelen}
|
||||
}
|
||||
};
|
||||
|
||||
const int rv{ngtcp2_conn_read_pkt(c->quicConnection.get(), &path, &pi, buf.data(),
|
||||
static_cast<std::size_t>(nread), timestamp())};
|
||||
if (rv != 0)
|
||||
throw std::runtime_error{fmt::format("ngtcp2_conn_read_pkt: {}", ngtcp2_strerror(rv))};
|
||||
}
|
||||
}
|
||||
|
||||
void ClientSendDatagram(CNetClientSession::Quic* c, const std::span<const std::uint8_t> data)
|
||||
{
|
||||
iovec iov{
|
||||
.iov_base = const_cast<std::uint8_t*>(data.data()),
|
||||
.iov_len = data.size(),
|
||||
};
|
||||
msghdr msg{};
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
ssize_t nwrite;
|
||||
do
|
||||
{
|
||||
nwrite = sendmsg(c->fd, &msg, 0);
|
||||
} while (nwrite == -1 && errno == EINTR);
|
||||
|
||||
if (nwrite == -1)
|
||||
throw std::runtime_error{fmt::format("sendmsg: {}", strerror(errno))};
|
||||
}
|
||||
|
||||
void ClientWriteStreams(CNetClientSession::Quic* c)
|
||||
{
|
||||
const ngtcp2_tstamp ts{timestamp()};
|
||||
ngtcp2_pkt_info pi;
|
||||
std::array<uint8_t, MAX_UDP_PAYLOAD_SIZE> buffer;
|
||||
|
||||
ngtcp2_path_storage ps;
|
||||
ngtcp2_path_storage_zero(&ps);
|
||||
std::uint32_t flags{NGTCP2_WRITE_STREAM_FLAG_MORE};
|
||||
|
||||
while (true)
|
||||
{
|
||||
ngtcp2_vec datav;
|
||||
std::int64_t streamId;
|
||||
|
||||
const auto bytesToSend = c->streams.has_value() ? c->streams.value().PeekData() : std::nullopt;
|
||||
if (c->streams.has_value() && bytesToSend.has_value())
|
||||
{
|
||||
datav.base = const_cast<uint8_t*>(bytesToSend->data());
|
||||
datav.len = bytesToSend->size();
|
||||
streamId = c->streams.value().m_Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
datav.base = nullptr;
|
||||
datav.len = 0;
|
||||
streamId = -1;
|
||||
if (c->streams.has_value())
|
||||
flags &= ~NGTCP2_WRITE_STREAM_FLAG_MORE;
|
||||
}
|
||||
|
||||
ngtcp2_ssize wdatalen;
|
||||
const ngtcp2_ssize nwrite = ngtcp2_conn_writev_stream(c->quicConnection.get(), &ps.path, &pi,
|
||||
buffer.data(), buffer.size(), &wdatalen, flags, streamId, &datav, 1, ts);
|
||||
if (nwrite < 0)
|
||||
{
|
||||
if (nwrite == NGTCP2_ERR_STREAM_DATA_BLOCKED)
|
||||
{
|
||||
LOGWARNING("blocked");
|
||||
break;
|
||||
}
|
||||
if (nwrite != NGTCP2_ERR_WRITE_MORE)
|
||||
{
|
||||
throw std::runtime_error{fmt::format("ngtcp2_conn_writev_stream: {}",
|
||||
ngtcp2_strerror(static_cast<int>(nwrite)))};
|
||||
}
|
||||
if (c->streams.has_value() && wdatalen > 0)
|
||||
c->streams.value().MarkSent(wdatalen);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nwrite == 0)
|
||||
break;
|
||||
|
||||
if (c->streams.has_value() && wdatalen > 0)
|
||||
c->streams.value().MarkSent(wdatalen);
|
||||
|
||||
ClientSendDatagram(c, {buffer.data(), static_cast<std::size_t>(nwrite)});
|
||||
}
|
||||
ngtcp2_conn_update_pkt_tx_time(c->quicConnection.get(), timestamp());
|
||||
}
|
||||
|
||||
void ClientHandleExpiry(CNetClientSession::Quic* c)
|
||||
{
|
||||
if (const int rv = ngtcp2_conn_handle_expiry(c->quicConnection.get(), timestamp()))
|
||||
throw std::runtime_error{fmt::format("ngtcp2_conn_handle_expiry: {}", ngtcp2_strerror(rv))};
|
||||
}
|
||||
|
||||
ngtcp2_conn *GetConnection(ngtcp2_crypto_conn_ref* conn_ref)
|
||||
{
|
||||
CNetClientSession::Quic* c{static_cast<CNetClientSession::Quic*>(conn_ref->user_data)};
|
||||
return c->quicConnection.get();
|
||||
}
|
||||
|
||||
void ClientInit(CNetClientSession& session, const char* host, const std::uint16_t port)
|
||||
{
|
||||
*session.m_Quic = CNetClientSession::Quic{};
|
||||
|
||||
const auto [descriptor, remoteAddress] = CreateSocket(host, port);
|
||||
session.m_Quic->fd = descriptor;
|
||||
|
||||
session.m_Quic->localAddress = ConnectSocket(session.m_Quic->fd, remoteAddress);
|
||||
|
||||
ClientGnutlsInit(session.m_Quic.get());
|
||||
|
||||
ClientQuicInit(session, remoteAddress, session.m_Quic->localAddress);
|
||||
|
||||
session.m_Quic->connectionReference.get_conn = &GetConnection;
|
||||
session.m_Quic->connectionReference.user_data = session.m_Quic.get();
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
CNetClientSession::CNetClientSession(CNetClient& client) :
|
||||
m_Client(client), m_FileTransferer(*this)
|
||||
m_Client(client), m_FileTransferer(*this),
|
||||
m_Quic(std::make_unique<Quic>())
|
||||
{
|
||||
}
|
||||
|
||||
CNetClientSession::~CNetClientSession()
|
||||
{
|
||||
ENSURE(!m_LoopRunning);
|
||||
|
||||
constexpr ngtcp2_ccerr reason{
|
||||
.type{NGTCP2_CCERR_TYPE_TRANSPORT},
|
||||
.error_code{0}
|
||||
};
|
||||
|
||||
std::array<std::uint8_t, MAX_UDP_PAYLOAD_SIZE> buffer;
|
||||
const ngtcp2_ssize amount{ngtcp2_conn_write_connection_close(m_Quic->quicConnection.get(), nullptr, nullptr, buffer.data(),
|
||||
buffer.size(), &reason, timestamp())};
|
||||
if (amount <= 0)
|
||||
{
|
||||
LOGERROR("closing connection %s", ngtcp2_strerror(amount));
|
||||
}
|
||||
|
||||
ClientSendDatagram(m_Quic.get(), {buffer.data(), static_cast<std::size_t>(amount)});
|
||||
}
|
||||
|
||||
bool CNetClientSession::Connect(const CStr& server, const u16 port, ENetHost* enetClient)
|
||||
bool CNetClientSession::Connect(const CStr& server, const u16 port)
|
||||
{
|
||||
ENSURE(!m_LoopRunning);
|
||||
ENSURE(!m_Host);
|
||||
ENSURE(!m_Server);
|
||||
|
||||
// Create ENet host if necessary.
|
||||
m_Host.reset(enetClient != nullptr ? enetClient : PS::Enet::CreateHost(nullptr, 1, CHANNEL_COUNT));
|
||||
ClientInit(*this, server.c_str(), port);
|
||||
ClientWriteStreams(m_Quic.get());
|
||||
|
||||
if (!m_Host)
|
||||
return false;
|
||||
|
||||
// Bind to specified host
|
||||
ENetAddress addr;
|
||||
addr.port = port;
|
||||
if (enet_address_set_host(&addr, server.c_str()) < 0)
|
||||
return false;
|
||||
|
||||
// Initiate connection to server
|
||||
m_Server.reset(enet_host_connect(m_Host.get(), &addr, CHANNEL_COUNT, 0));
|
||||
if (!m_Server)
|
||||
return false;
|
||||
|
||||
|
||||
m_Stats = std::make_unique<CNetStatsTable>(*m_Server);
|
||||
m_Stats = std::make_unique<CNetStatsTable>(m_Quic->quicConnection.get());
|
||||
if (CProfileViewer::IsInitialised())
|
||||
g_ProfileViewer.AddRootTable(m_Stats.get());
|
||||
|
||||
|
|
@ -84,14 +440,24 @@ void CNetClientSession::RunNetLoop(CNetClientSession* session)
|
|||
|
||||
while (!session->m_ShouldShutdown)
|
||||
{
|
||||
ENSURE(session->m_Host && session->m_Server);
|
||||
// ENSURE(session->m_Host && session->m_Server);
|
||||
|
||||
session->m_FileTransferer.Poll();
|
||||
session->Poll();
|
||||
try {
|
||||
session->Poll();
|
||||
}
|
||||
catch (std::runtime_error&)
|
||||
{
|
||||
// Report immediately.
|
||||
LOGMESSAGE("Net client: Disconnected");
|
||||
session->m_Connected = false;
|
||||
session->m_IncomingMessages.push(Disconnect{});
|
||||
return;
|
||||
}
|
||||
session->Flush();
|
||||
|
||||
session->m_LastReceivedTime = enet_time_get() - session->m_Server->lastReceiveTime;
|
||||
session->m_MeanRTT = session->m_Server->roundTripTime;
|
||||
// session->m_LastReceivedTime = timestamp() - session->m_Server->lastReceiveTime;
|
||||
// session->m_MeanRTT = session->m_Server->roundTripTime;
|
||||
}
|
||||
|
||||
session->m_LoopRunning = false;
|
||||
|
|
@ -107,96 +473,77 @@ void CNetClientSession::Shutdown()
|
|||
|
||||
void CNetClientSession::Poll()
|
||||
{
|
||||
ENetEvent event;
|
||||
pollfd pfd{
|
||||
.fd{m_Quic->fd},
|
||||
.events{POLLIN}
|
||||
};
|
||||
|
||||
// Use the timeout to make the thread wait and save CPU time.
|
||||
if (enet_host_service(m_Host.get(), &event, NETCLIENT_POLL_TIMEOUT) <= 0)
|
||||
const int ret{poll(&pfd, 1, NETCLIENT_POLL_TIMEOUT)};
|
||||
if (ret < 0)
|
||||
{
|
||||
LOGERROR("Error while waiting for poll: %s", std::strerror(errno));
|
||||
return;
|
||||
|
||||
if (event.type == ENET_EVENT_TYPE_CONNECT)
|
||||
{
|
||||
ENSURE(event.peer == m_Server.get());
|
||||
|
||||
// Report the server address immediately.
|
||||
char hostname[256] = "(error)";
|
||||
enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
|
||||
LOGMESSAGE("Net client: Connected to %s:%u", hostname, (unsigned int)event.peer->address.port);
|
||||
m_Connected = true;
|
||||
m_WasConnected = true;
|
||||
|
||||
m_IncomingMessages.push(event);
|
||||
}
|
||||
else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
|
||||
if (ret == 0)
|
||||
{
|
||||
ENSURE(event.peer == m_Server.get());
|
||||
|
||||
// Report immediately.
|
||||
LOGMESSAGE("Net client: Disconnected");
|
||||
m_Connected = false;
|
||||
|
||||
m_IncomingMessages.push(event);
|
||||
ClientHandleExpiry(m_Quic.get());
|
||||
ClientWriteStreams(m_Quic.get());
|
||||
return;
|
||||
}
|
||||
else if (event.type == ENET_EVENT_TYPE_RECEIVE)
|
||||
m_IncomingMessages.push(event);
|
||||
|
||||
ClientRead(m_Quic.get());
|
||||
ClientWriteStreams(m_Quic.get());
|
||||
}
|
||||
|
||||
void CNetClientSession::Flush()
|
||||
{
|
||||
ENetPacket* packet;
|
||||
while (m_OutgoingMessages.pop(packet))
|
||||
if (enet_peer_send(m_Server.get(), CNetHost::DEFAULT_CHANNEL, packet) < 0)
|
||||
{
|
||||
// Report the error, but do so silently if we know we are disconnected.
|
||||
if (m_Connected)
|
||||
LOGERROR("NetClient: Failed to send packet to server");
|
||||
else
|
||||
LOGMESSAGE("NetClient: Failed to send packet to server");
|
||||
}
|
||||
|
||||
enet_host_flush(m_Host.get());
|
||||
std::vector<std::uint8_t>* message;
|
||||
while (m_OutgoingMessages.pop(message))
|
||||
{
|
||||
std::unique_ptr<std::vector<std::uint8_t>> data{message};
|
||||
if (m_Quic->streams.has_value())
|
||||
m_Quic->streams.value().PushData(std::move(*data));
|
||||
else
|
||||
LOGERROR("no stream to send message");
|
||||
}
|
||||
}
|
||||
|
||||
void CNetClientSession::ProcessPolledMessages()
|
||||
{
|
||||
ENetEvent event;
|
||||
while(m_IncomingMessages.pop(event))
|
||||
IncommingMessage query{};
|
||||
while(m_IncomingMessages.pop(query))
|
||||
{
|
||||
if (event.type == ENET_EVENT_TYPE_CONNECT)
|
||||
m_Client.HandleConnect();
|
||||
else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
|
||||
std::visit([&]<typename Message>(Message message)
|
||||
{
|
||||
// This deletes the session, so we must break;
|
||||
if (event.data == 0 && !m_WasConnected)
|
||||
m_Client.HandleDisconnect(NDR_CONNECTION_REQUEST_TIMED_OUT);
|
||||
else
|
||||
m_Client.HandleDisconnect(event.data);
|
||||
break;
|
||||
}
|
||||
else if (event.type == ENET_EVENT_TYPE_RECEIVE)
|
||||
{
|
||||
CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, m_Client.GetScriptInterface());
|
||||
if (msg)
|
||||
if constexpr (std::same_as<Message, ConnectionEstablished>)
|
||||
{
|
||||
LOGMESSAGE("Net client: Received message %s of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
|
||||
|
||||
m_Client.HandleMessage(msg);
|
||||
m_Client.HandleConnect();
|
||||
}
|
||||
// Thread-safe
|
||||
enet_packet_destroy(event.packet);
|
||||
}
|
||||
else if constexpr (std::same_as<Message, Disconnect>)
|
||||
{
|
||||
m_Client.HandleDisconnect(NDR_UNKNOWN);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::same_as<Message, std::vector<std::uint8_t>*>);
|
||||
std::unique_ptr<std::vector<std::uint8_t>> data{message};
|
||||
CNetMessage* msg = CNetMessageFactory::CreateMessage(*data, m_Client.GetScriptInterface());
|
||||
if (msg)
|
||||
{
|
||||
LOGMESSAGE("Net client: Received message %s of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
|
||||
|
||||
m_Client.HandleMessage(msg);
|
||||
}
|
||||
}
|
||||
}, query);
|
||||
}
|
||||
}
|
||||
|
||||
bool CNetClientSession::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
ENSURE(m_Host && m_Server);
|
||||
// ENSURE(m_Host && m_Server);
|
||||
|
||||
// Thread-safe.
|
||||
ENetPacket* packet = CNetHost::CreatePacket(message);
|
||||
if (!packet)
|
||||
return false;
|
||||
|
||||
if (!m_OutgoingMessages.push(packet))
|
||||
if (!m_OutgoingMessages.push(new std::vector{CNetHost::CreatePacket(message)}))
|
||||
{
|
||||
LOGERROR("NetClient: Failed to push message on the outgoing queue.");
|
||||
return false;
|
||||
|
|
@ -207,17 +554,11 @@ bool CNetClientSession::SendMessage(const CNetMessage* message)
|
|||
|
||||
u32 CNetClientSession::GetLastReceivedTime() const
|
||||
{
|
||||
if (!m_Server)
|
||||
return 0;
|
||||
|
||||
return m_LastReceivedTime;
|
||||
}
|
||||
|
||||
u32 CNetClientSession::GetMeanRTT() const
|
||||
{
|
||||
if (!m_Server)
|
||||
return 0;
|
||||
|
||||
return m_MeanRTT;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
#define NETSESSION_H
|
||||
|
||||
#include "lib/code_annotation.h"
|
||||
#include "lib/external_libraries/enet.h"
|
||||
#include "lib/types.h"
|
||||
#include "network/NetFileTransfer.h"
|
||||
#include "network/NetHost.h"
|
||||
|
|
@ -32,8 +31,6 @@ class CNetMessage;
|
|||
class CNetStatsTable;
|
||||
class CStr;
|
||||
|
||||
typedef struct _ENetHost ENetHost;
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Network client/server sessions.
|
||||
|
|
@ -53,10 +50,11 @@ class CNetClientSession
|
|||
NONCOPYABLE(CNetClientSession);
|
||||
|
||||
public:
|
||||
struct Quic;
|
||||
CNetClientSession(CNetClient& client);
|
||||
~CNetClientSession();
|
||||
|
||||
bool Connect(const CStr& server, const u16 port, ENetHost* enetClient);
|
||||
bool Connect(const CStr& server, const u16 port);
|
||||
|
||||
/**
|
||||
* The client NetSession is threaded to avoid getting timeouts if the main thread hangs.
|
||||
|
|
@ -104,11 +102,14 @@ private:
|
|||
CNetClient& m_Client;
|
||||
|
||||
CNetFileTransferer m_FileTransferer;
|
||||
|
||||
public:
|
||||
// Net messages received and waiting for fetching.
|
||||
boost::lockfree::queue<ENetEvent> m_IncomingMessages{16};
|
||||
struct ConnectionEstablished{};
|
||||
struct Disconnect{};
|
||||
using IncommingMessage = std::variant<ConnectionEstablished, std::vector<std::uint8_t>*, Disconnect>;
|
||||
boost::lockfree::queue<IncommingMessage> m_IncomingMessages{16};
|
||||
// Net messages to send on the next flush() call.
|
||||
boost::lockfree::queue<ENetPacket*> m_OutgoingMessages{16};
|
||||
boost::lockfree::queue<std::vector<std::uint8_t>*> m_OutgoingMessages{16};
|
||||
|
||||
// Last known state. If false, flushing errors are silenced.
|
||||
bool m_Connected{false};
|
||||
|
|
@ -116,7 +117,7 @@ private:
|
|||
// Whether this session was ever connected to the server.
|
||||
bool m_WasConnected{false};
|
||||
|
||||
// Wrapper around enet stats - those are atomic as the code is lock-free.
|
||||
// Wrapper around stats - those are atomic as the code is lock-free.
|
||||
std::atomic<u32> m_LastReceivedTime{0};
|
||||
std::atomic<u32> m_MeanRTT{0};
|
||||
|
||||
|
|
@ -124,9 +125,9 @@ private:
|
|||
std::atomic<bool> m_LoopRunning{false};
|
||||
std::atomic<bool> m_ShouldShutdown{false};
|
||||
|
||||
std::unique_ptr<ENetHost, DestroyHost> m_Host;
|
||||
std::unique_ptr<ENetPeer, DestroyPeer> m_Server;
|
||||
std::unique_ptr<CNetStatsTable> m_Stats;
|
||||
|
||||
const std::unique_ptr<Quic> m_Quic;
|
||||
};
|
||||
|
||||
#endif // NETSESSION_H
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -25,32 +25,10 @@
|
|||
#include "ps/CLogger.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
bool CNetHost::SendMessage(const CNetMessage* message, ENetPeer* peer, const char* peerName)
|
||||
{
|
||||
ENetPacket* packet = CreatePacket(message);
|
||||
if (!packet)
|
||||
return false;
|
||||
|
||||
LOGMESSAGE("Net: Sending message %s of size %lu to %s", message->ToString().c_str(), (unsigned long)packet->dataLength, peerName);
|
||||
|
||||
// Let ENet send the message to peer
|
||||
if (enet_peer_send(peer, DEFAULT_CHANNEL, packet) < 0)
|
||||
{
|
||||
LOGERROR("Net: Failed to send packet to peer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't call enet_host_flush now - let it queue up all the packets
|
||||
// and send them during the next frame
|
||||
//
|
||||
// TODO: we should flush explicitly at some appropriate point before the next frame
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ENetPacket* CNetHost::CreatePacket(const CNetMessage* message)
|
||||
std::vector<std::uint8_t> CNetHost::CreatePacket(const CNetMessage* message)
|
||||
{
|
||||
size_t size = message->GetSerializedLength();
|
||||
|
||||
|
|
@ -63,12 +41,7 @@ ENetPacket* CNetHost::CreatePacket(const CNetMessage* message)
|
|||
// Save message to internal buffer
|
||||
message->Serialize(&buffer[0]);
|
||||
|
||||
// Create a reliable packet
|
||||
ENetPacket* packet = enet_packet_create(&buffer[0], size, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (!packet)
|
||||
LOGERROR("Net: Failed to construct packet");
|
||||
|
||||
return packet;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void CNetHost::Initialize()
|
||||
|
|
@ -81,3 +54,81 @@ void CNetHost::Deinitialize()
|
|||
{
|
||||
enet_deinitialize();
|
||||
}
|
||||
|
||||
|
||||
Stream::Stream(const std::int64_t streamId):
|
||||
m_Id{streamId}
|
||||
{}
|
||||
|
||||
void Stream::PushData(std::vector<std::uint8_t> data)
|
||||
{
|
||||
m_SendBuffer.push_back(std::move(data));
|
||||
}
|
||||
|
||||
void Stream::PushMessage(const CNetMessage* message)
|
||||
{
|
||||
m_SendBuffer.push_back(CNetHost::CreatePacket(message));
|
||||
}
|
||||
|
||||
std::optional<std::span<const std::uint8_t>> Stream::PeekData()
|
||||
{
|
||||
const std::size_t startOffset{m_SentOffset - m_AckedOffset};
|
||||
std::size_t offset{0};
|
||||
|
||||
for (std::vector<std::uint8_t>& bytes : m_SendBuffer)
|
||||
{
|
||||
if (startOffset - offset < bytes.size())
|
||||
{
|
||||
const std::size_t temp{startOffset - offset};
|
||||
return std::span{bytes.data() + temp, bytes.size() - temp};
|
||||
}
|
||||
|
||||
offset += bytes.size();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Stream::MarkSent(const std::size_t offset)
|
||||
{
|
||||
m_SentOffset += offset;
|
||||
}
|
||||
|
||||
void Stream::MarkAcknowledged(const std::size_t offset)
|
||||
{
|
||||
while (!m_SendBuffer.empty())
|
||||
{
|
||||
std::vector<uint8_t>& head{m_SendBuffer.front()};
|
||||
if (m_AckedOffset + head.size() > offset)
|
||||
break;
|
||||
|
||||
m_AckedOffset += head.size();
|
||||
m_SendBuffer.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::uint8_t>> Stream::Receive(const std::span<const std::uint8_t> data)
|
||||
{
|
||||
m_ReceiveBuffer.emplace_back(data.begin(), data.end());
|
||||
const std::size_t bufferSize{std::transform_reduce(m_ReceiveBuffer.begin(), m_ReceiveBuffer.end(),
|
||||
static_cast<std::size_t>(0), std::plus<>{}, std::mem_fn(&std::vector<std::uint8_t>::size))};
|
||||
if (bufferSize < 3)
|
||||
return std::nullopt;
|
||||
const auto& message = m_ReceiveBuffer.front();
|
||||
|
||||
auto bufferIter = message.begin();
|
||||
std::size_t messageSize;
|
||||
Deserialize_int_1(bufferIter, std::ignore);
|
||||
Deserialize_int_2(bufferIter, messageSize);
|
||||
if (messageSize > bufferSize)
|
||||
return std::nullopt;
|
||||
|
||||
std::vector<std::uint8_t> messageCopy;
|
||||
while (messageCopy.size() < messageSize)
|
||||
{
|
||||
messageCopy.insert(messageCopy.end(), m_ReceiveBuffer.front().begin(),
|
||||
m_ReceiveBuffer.front().end());
|
||||
m_ReceiveBuffer.pop_front();
|
||||
}
|
||||
return messageCopy;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,14 @@
|
|||
#include "lib/types.h"
|
||||
#include "ps/CStr.h"
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
class CNetMessage;
|
||||
|
||||
|
|
@ -35,6 +42,38 @@ typedef struct _ENetPeer ENetPeer;
|
|||
typedef struct _ENetPacket ENetPacket;
|
||||
typedef struct _ENetHost ENetHost;
|
||||
|
||||
constexpr std::size_t MAX_UDP_PAYLOAD_SIZE{64 * KiB};
|
||||
|
||||
struct CredentialsDeleter
|
||||
{
|
||||
void operator()(const gnutls_certificate_credentials_t cred) const
|
||||
{
|
||||
gnutls_certificate_free_credentials(cred);
|
||||
}
|
||||
};
|
||||
|
||||
struct SessionDeleter
|
||||
{
|
||||
void operator()(const gnutls_session_t p) const
|
||||
{
|
||||
gnutls_deinit(p);
|
||||
}
|
||||
};
|
||||
|
||||
struct ConnectionDeleter
|
||||
{
|
||||
void operator()(ngtcp2_conn* p) const
|
||||
{
|
||||
ngtcp2_conn_del(p);
|
||||
}
|
||||
};
|
||||
|
||||
struct AddressStorage
|
||||
{
|
||||
ngtcp2_sockaddr_union address;
|
||||
ngtcp2_socklen length{sizeof(address)};
|
||||
};
|
||||
|
||||
struct PlayerAssignment
|
||||
{
|
||||
/**
|
||||
|
|
@ -106,20 +145,11 @@ class CNetHost
|
|||
public:
|
||||
static const int DEFAULT_CHANNEL = 0;
|
||||
|
||||
/**
|
||||
* Transmit a message to the given peer.
|
||||
* @param message message to send
|
||||
* @param peer peer to send to
|
||||
* @param peerName name of peer for debug logs
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
static bool SendMessage(const CNetMessage* message, ENetPeer* peer, const char* peerName);
|
||||
|
||||
/**
|
||||
* Construct an ENet packet by serialising the given message.
|
||||
* @return NULL on failure
|
||||
*/
|
||||
static ENetPacket* CreatePacket(const CNetMessage* message);
|
||||
static std::vector<std::uint8_t> CreatePacket(const CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Initialize ENet.
|
||||
|
|
@ -133,4 +163,25 @@ public:
|
|||
static void Deinitialize();
|
||||
};
|
||||
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream(const std::int64_t streamId);
|
||||
|
||||
void PushData(std::vector<std::uint8_t> data);
|
||||
void PushMessage(const CNetMessage* message);
|
||||
std::optional<std::span<const std::uint8_t>> PeekData();
|
||||
void MarkSent(const std::size_t offset);
|
||||
void MarkAcknowledged(const std::size_t offset);
|
||||
std::optional<std::vector<std::uint8_t>> Receive(const std::span<const std::uint8_t> data);
|
||||
|
||||
std::int64_t m_Id;
|
||||
private:
|
||||
std::deque<std::vector<std::uint8_t>> m_SendBuffer;
|
||||
std::deque<std::vector<std::uint8_t>> m_ReceiveBuffer;
|
||||
/* invariant: m_SentOffset >= m_AckedOffset */
|
||||
std::size_t m_SentOffset{0};
|
||||
std::size_t m_AckedOffset{0};
|
||||
};
|
||||
|
||||
#endif // NETHOST_H
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -53,10 +53,7 @@ u8* CNetMessage::Serialize(u8* pBuffer) const
|
|||
const u8* CNetMessage::Deserialize(const u8* pStart, const u8* pEnd)
|
||||
{
|
||||
if (pStart + 3 > pEnd)
|
||||
{
|
||||
LOGERROR("CNetMessage: Corrupt packet (smaller than header)");
|
||||
return NULL;
|
||||
}
|
||||
throw std::invalid_argument{"CNetMessage: Corrupt packet (smaller than header)"};
|
||||
|
||||
const u8* pBuffer = pStart;
|
||||
|
||||
|
|
@ -67,10 +64,7 @@ const u8* CNetMessage::Deserialize(const u8* pStart, const u8* pEnd)
|
|||
m_Type = (NetMessageType)type;
|
||||
|
||||
if (pStart + size != pEnd)
|
||||
{
|
||||
LOGERROR("CNetMessage: Corrupt packet (incorrect size)");
|
||||
return NULL;
|
||||
}
|
||||
throw std::invalid_argument{fmt::format("CNetMessage: Corrupt packet (incorrect size) %i %i", size, pEnd - pStart)};
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
|
@ -91,15 +85,14 @@ CStr CNetMessage::ToString() const
|
|||
return "Unknown Message " + CStr::FromInt(GetType());
|
||||
}
|
||||
|
||||
CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
size_t dataSize,
|
||||
const ScriptInterface& scriptInterface)
|
||||
CNetMessage* CNetMessageFactory::CreateMessage(const std::span<const std::uint8_t> data,
|
||||
const ScriptInterface& scriptInterface)
|
||||
{
|
||||
CNetMessage* pNewMessage = NULL;
|
||||
CNetMessage header;
|
||||
|
||||
// Figure out message type
|
||||
header.Deserialize((const u8*)pData, (const u8*)pData + dataSize);
|
||||
header.Deserialize(std::to_address(data.begin()), std::to_address(data.end()));
|
||||
|
||||
switch (header.GetType())
|
||||
{
|
||||
|
|
@ -230,7 +223,7 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
|||
}
|
||||
|
||||
if (pNewMessage)
|
||||
pNewMessage->Deserialize((const u8*)pData, (const u8*)pData + dataSize);
|
||||
pNewMessage->Deserialize(std::to_address(data.begin()), std::to_address(data.end()));
|
||||
|
||||
return pNewMessage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include <js/RootingAPI.h>
|
||||
#include <js/TypeDecls.h>
|
||||
#include <js/Value.h>
|
||||
#include <span>
|
||||
|
||||
class ScriptInterface;
|
||||
|
||||
|
|
@ -108,12 +109,12 @@ public:
|
|||
/**
|
||||
* Factory method which creates a message object based on the given data
|
||||
*
|
||||
* @param pData Data buffer
|
||||
* @param dataSize Size of data buffer
|
||||
* @param data Data buffer
|
||||
* @param scriptInterface Script instance to use when constructing scripted messages
|
||||
* @return The new message created
|
||||
*/
|
||||
static CNetMessage* CreateMessage(const void* pData, size_t dataSize, const ScriptInterface& scriptInterface);
|
||||
static CNetMessage* CreateMessage(const std::span<const std::uint8_t> data,
|
||||
const ScriptInterface& scriptInterface);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
#include <js/Value.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
class ScriptInterface;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -19,10 +19,14 @@
|
|||
|
||||
#include "NetProtocol.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/CStr.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -59,3 +63,36 @@ std::optional<HandshakeError> CheckHandshake(const CSrvHandshakeMessage& serverM
|
|||
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t timestamp()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
void increaseWindow(ngtcp2_conn* conn, const std::uint64_t streamId, const std::uint64_t size)
|
||||
{
|
||||
ngtcp2_conn_extend_max_offset(conn, size);
|
||||
ngtcp2_conn_extend_max_stream_offset(conn, streamId, size);
|
||||
}
|
||||
|
||||
void OnRandomRequest(std::uint8_t* destination, const std::size_t destinationLength, const ngtcp2_rand_ctx*)
|
||||
{
|
||||
const int ret{gnutls_rnd(GNUTLS_RND_RANDOM, destination, destinationLength)};
|
||||
if (ret < 0)
|
||||
LOGERROR("gnutls_rnd: %s", gnutls_strerror(ret));
|
||||
}
|
||||
|
||||
int OnNewConnectionIdRequest(ngtcp2_conn*, ngtcp2_cid* cid, std::uint8_t* token, const std::size_t cidlen,
|
||||
void* /*userData*/)
|
||||
{
|
||||
if (gnutls_rnd(GNUTLS_RND_RANDOM, cid->data, cidlen))
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
|
||||
cid->datalen = cidlen;
|
||||
|
||||
if (gnutls_rnd (GNUTLS_RND_RANDOM, token, NGTCP2_STATELESS_RESET_TOKENLEN))
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,11 +27,17 @@
|
|||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
/**
|
||||
* Report the peer if we didn't receive a packet after this time (milliseconds).
|
||||
*/
|
||||
inline constexpr u32 NETWORK_WARNING_TIMEOUT{2000};
|
||||
|
||||
inline constexpr const char* TLS_PRIORITY{
|
||||
"PERFORMANCE:-VERS-DTLS-ALL:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-SHA1:-AES-128-CBC:-AES-256-CBC:"
|
||||
"-SIGN-RSA-SHA1:-SIGN-ECDSA-SHA1:%DISABLE_TLS13_COMPAT_MODE"};
|
||||
|
||||
struct HandshakeError
|
||||
{
|
||||
std::string componentType;
|
||||
|
|
@ -67,4 +73,17 @@ Message CreateHandshake() {
|
|||
|
||||
std::optional<HandshakeError> CheckHandshake(const CSrvHandshakeMessage& serverMessage, const CCliHandshakeMessage& clientMessage);
|
||||
|
||||
uint64_t timestamp();
|
||||
|
||||
struct ngtcp2_conn;
|
||||
void increaseWindow(ngtcp2_conn* conn, const std::uint64_t streamId, const std::uint64_t size);
|
||||
|
||||
struct ngtcp2_rand_ctx;
|
||||
void OnRandomRequest(std::uint8_t* destination, const std::size_t destinationLength,
|
||||
const ngtcp2_rand_ctx*);
|
||||
|
||||
struct ngtcp2_cid;
|
||||
int OnNewConnectionIdRequest(ngtcp2_conn*, ngtcp2_cid* cid, std::uint8_t *token, const std::size_t cidlen,
|
||||
void* /*userData*/);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "lib/code_generation.h"
|
||||
#include "lib/debug.h"
|
||||
#include "lib/external_libraries/enet.h"
|
||||
#include "lib/hash.h"
|
||||
#include "lib/secure_crt.h"
|
||||
#include "lib/status.h"
|
||||
#include "lib/types.h"
|
||||
|
|
@ -54,8 +55,17 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <netdb.h>
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <ngtcp2/ngtcp2_crypto.h>
|
||||
#include <ngtcp2/ngtcp2_crypto_gnutls.h>
|
||||
#include <numeric>
|
||||
#include <poll.h>
|
||||
#include <ranges>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
|
@ -97,15 +107,256 @@ constexpr u32 NETWORK_BAD_PING = DEFAULT_TURN_LENGTH * COMMAND_DELAY_MP / 2;
|
|||
|
||||
CNetServer* g_NetServer = NULL;
|
||||
|
||||
static CStr DebugName(CNetServerSession* session)
|
||||
namespace
|
||||
{
|
||||
if (session == NULL)
|
||||
return "[unknown host]";
|
||||
if (session->GetGUID().empty())
|
||||
return "[unauthed host]";
|
||||
return "[" + session->GetGUID().substr(0, 8) + "...]";
|
||||
// static CStr DebugName(CNetServerSession* session)
|
||||
// {
|
||||
// if (session == NULL)
|
||||
// return "[unknown host]";
|
||||
// if (session->GetGUID().empty())
|
||||
// return "[unauthed host]";
|
||||
// return "[" + session->GetGUID().substr(0, 8) + "...]";
|
||||
// }
|
||||
|
||||
struct CidHash
|
||||
{
|
||||
std::size_t operator()(const ngtcp2_cid& cid) const
|
||||
{
|
||||
return std::accumulate(cid.data, cid.data + cid.datalen, static_cast<std::size_t>(0),
|
||||
[](std::size_t carry, const std::uint8_t byte)
|
||||
{
|
||||
hash_combine(carry, byte);
|
||||
return carry;
|
||||
});
|
||||
}
|
||||
};
|
||||
struct CidEqual
|
||||
{
|
||||
bool operator()(const ngtcp2_cid& a, const ngtcp2_cid& b) const
|
||||
{
|
||||
return ngtcp2_cid_eq(&a, &b);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t ReceivePackage(const int fd, std::span<std::uint8_t> data, AddressStorage& remoteAddress)
|
||||
{
|
||||
iovec iov{
|
||||
.iov_base{data.data()},
|
||||
.iov_len{data.size()}
|
||||
};
|
||||
|
||||
msghdr msg{};
|
||||
msg.msg_name = &remoteAddress.address.sa;
|
||||
msg.msg_namelen = remoteAddress.length;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
ssize_t ret;
|
||||
do
|
||||
ret = recvmsg(fd, &msg, MSG_DONTWAIT);
|
||||
while (ret < 0 && errno == EINTR);
|
||||
if (ret < 0)
|
||||
throw std::system_error{errno, std::generic_category(), "recvmsg"};
|
||||
|
||||
remoteAddress.length = msg.msg_namelen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GetRandomCid(ngtcp2_cid* cid)
|
||||
{
|
||||
std::array<std::uint8_t, NGTCP2_MAX_CIDLEN> buf;
|
||||
const int ret{gnutls_rnd(GNUTLS_RND_RANDOM, buf.data(), buf.size())};
|
||||
if (ret < 0)
|
||||
throw std::runtime_error{fmt::format("gnutls_rnd: {}", gnutls_strerror(ret))};
|
||||
ngtcp2_cid_init(cid, buf.data(), buf.size());
|
||||
}
|
||||
|
||||
int ResolveAndBind(const char *port, AddressStorage& localAddress)
|
||||
{
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
addrinfo* result;
|
||||
if (getaddrinfo("127.0.0.1", port, &hints, &result))
|
||||
return -1;
|
||||
|
||||
int fd;
|
||||
addrinfo* rp;
|
||||
for (rp = result; rp; rp = rp->ai_next)
|
||||
{
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
if (bind(fd, rp->ai_addr, rp->ai_addrlen))
|
||||
{
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (!rp)
|
||||
return -1;
|
||||
|
||||
memcpy(&localAddress.address, rp->ai_addr, rp->ai_addrlen);
|
||||
localAddress.length = rp->ai_addrlen;
|
||||
return fd;
|
||||
}
|
||||
|
||||
std::unique_ptr<gnutls_certificate_credentials_st, CredentialsDeleter> CreateTlsServerCredentials()
|
||||
{
|
||||
gnutls_certificate_credentials_t temp;
|
||||
if (const int ret{gnutls_certificate_allocate_credentials(&temp)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("gnutls_certificate_allocate_credentials: {}",
|
||||
gnutls_strerror(ret))};
|
||||
}
|
||||
std::unique_ptr<gnutls_certificate_credentials_st, CredentialsDeleter> cred{temp};
|
||||
|
||||
if (const int ret{gnutls_certificate_set_x509_key_file(cred.get(), "certificate.pem",
|
||||
"privatekey.pem", GNUTLS_X509_FMT_PEM)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("gnutls_certificate_set_x509_key_file: {}",
|
||||
gnutls_strerror(ret))};
|
||||
}
|
||||
|
||||
return cred;
|
||||
}
|
||||
|
||||
ngtcp2_settings InitSettings()
|
||||
{
|
||||
ngtcp2_settings settings;
|
||||
ngtcp2_settings_default(&settings);
|
||||
settings.initial_ts = timestamp();
|
||||
return settings;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
class CNetServerWorker::Quic
|
||||
{
|
||||
public:
|
||||
explicit Quic(const std::uint16_t port):
|
||||
m_SocketFd{ResolveAndBind(std::to_string(port).c_str(), m_LocalAddress)}
|
||||
{}
|
||||
~Quic()
|
||||
{
|
||||
if (m_SocketFd >= 0)
|
||||
close(m_SocketFd);
|
||||
}
|
||||
AddressStorage m_LocalAddress;
|
||||
int m_SocketFd;
|
||||
std::unique_ptr<gnutls_certificate_credentials_st, CredentialsDeleter> m_Credentials{
|
||||
CreateTlsServerCredentials()};
|
||||
ngtcp2_settings m_Settings{InitSettings()};
|
||||
|
||||
void HandleIncoming(CNetServerWorker& server)
|
||||
{
|
||||
std::array<std::uint8_t, MAX_UDP_PAYLOAD_SIZE> buf;
|
||||
|
||||
while (true)
|
||||
{
|
||||
AddressStorage remoteAddress;
|
||||
|
||||
std::size_t n_read;
|
||||
try
|
||||
{
|
||||
n_read = ReceivePackage(m_SocketFd, buf, remoteAddress);
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
if (e.code().value() == EAGAIN || e.code().value() == EWOULDBLOCK)
|
||||
return;
|
||||
throw;
|
||||
}
|
||||
|
||||
ngtcp2_version_cid version;
|
||||
if (const int ret{ngtcp2_pkt_decode_version_cid(&version, buf.data(), n_read,
|
||||
NGTCP2_MAX_CIDLEN)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("ngtcp2_pkt_decode_version_cid: {}",
|
||||
ngtcp2_strerror(ret))};
|
||||
}
|
||||
|
||||
const ngtcp2_addr remote{
|
||||
.addr{&remoteAddress.address.sa},
|
||||
.addrlen{remoteAddress.length}
|
||||
};
|
||||
|
||||
/* Find any existing connection by DCID */
|
||||
ngtcp2_cid tempCid;
|
||||
ngtcp2_cid_init(&tempCid, version.dcid, version.dcidlen);
|
||||
|
||||
const auto connectionIter = std::ranges::find_if(server.m_Sessions,
|
||||
[&tempCid](const std::vector<ngtcp2_cid> association)
|
||||
{
|
||||
return std::ranges::any_of(association, [&tempCid](const ngtcp2_cid& elem)
|
||||
{
|
||||
return ngtcp2_cid_eq(&elem, &tempCid);
|
||||
});
|
||||
},
|
||||
[](auto& connection)
|
||||
{
|
||||
const std::size_t amount{ngtcp2_conn_get_scid(
|
||||
connection->m_Connection.m_QuicConnection.get(), nullptr)};
|
||||
std::vector<ngtcp2_cid> cids(amount);
|
||||
ngtcp2_conn_get_scid(connection->m_Connection.m_QuicConnection.get(),
|
||||
cids.data());
|
||||
return cids;
|
||||
});
|
||||
|
||||
const bool existing{connectionIter != server.m_Sessions.end()};
|
||||
if (!existing)
|
||||
{
|
||||
ngtcp2_pkt_hd header;
|
||||
if (ngtcp2_accept(&header, buf.data(), n_read))
|
||||
throw std::invalid_argument{"Failed parsing package header"};
|
||||
|
||||
ngtcp2_cid newScid;
|
||||
GetRandomCid(&newScid);
|
||||
|
||||
const ngtcp2_path path{
|
||||
.local{
|
||||
.addr{&m_LocalAddress.address.sa},
|
||||
.addrlen{m_LocalAddress.length}
|
||||
},
|
||||
.remote{remote}
|
||||
};
|
||||
|
||||
server.m_Sessions.push_back(std::make_unique<CNetServerSession>(server, m_SocketFd,
|
||||
m_Settings, m_Credentials.get(), header, newScid, path));
|
||||
server.SetupSession(server.m_Sessions.back().get());
|
||||
}
|
||||
auto& session{existing ? *connectionIter : server.m_Sessions.back()};
|
||||
|
||||
try
|
||||
{
|
||||
session->m_Connection.Read(remote, {buf.data(), n_read});
|
||||
}
|
||||
catch (const std::system_error&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (const std::runtime_error&)
|
||||
{
|
||||
const auto session = existing ? std::move(*connectionIter) :
|
||||
std::move(server.m_Sessions.back());
|
||||
if (existing)
|
||||
server.m_Sessions.erase(connectionIter);
|
||||
else
|
||||
server.m_Sessions.pop_back();
|
||||
|
||||
session->Update(static_cast<uint>(NMT_CONNECTION_LOST), nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX: We use some non-threadsafe functions from the worker thread.
|
||||
* See https://gitea.wildfiregames.com/0ad/0ad/issues/654
|
||||
|
|
@ -120,17 +371,17 @@ CNetServerWorker::CNetServerWorker(const bool continueSavedGame, std::uint16_t p
|
|||
m_Password{std::move(password)}
|
||||
{
|
||||
// Bind to default host
|
||||
ENetAddress addr;
|
||||
addr.host = ENET_HOST_ANY;
|
||||
addr.port = port;
|
||||
// ENetAddress addr;
|
||||
// addr.host = ENET_HOST_ANY;
|
||||
// addr.port = port;
|
||||
|
||||
// Create ENet server
|
||||
m_Host.reset(PS::Enet::CreateHost(&addr, MAX_CLIENTS, CHANNEL_COUNT));
|
||||
if (!m_Host)
|
||||
{
|
||||
LOGERROR("Net server: enet_host_create failed");
|
||||
throw std::runtime_error{"Failed to start server"};
|
||||
}
|
||||
// m_Host.reset(PS::Enet::CreateHost(&addr, MAX_CLIENTS, CHANNEL_COUNT));
|
||||
// if (!m_Host)
|
||||
// {
|
||||
// LOGERROR("Net server: enet_host_create failed");
|
||||
// throw std::runtime_error{"Failed to start server"};
|
||||
// }
|
||||
|
||||
m_Stats = std::make_unique<CNetStatsTable>();
|
||||
if (CProfileViewer::IsInitialised())
|
||||
|
|
@ -140,7 +391,7 @@ CNetServerWorker::CNetServerWorker(const bool continueSavedGame, std::uint16_t p
|
|||
|
||||
// Launch the worker thread
|
||||
m_WorkerThread = std::thread(Threading::HandleExceptions<RunThread>::Wrapper, this,
|
||||
std::move(initAttributes));
|
||||
std::move(initAttributes), port);
|
||||
|
||||
#if CONFIG2_MINIUPNPC
|
||||
// Launch the UPnP thread
|
||||
|
|
@ -163,10 +414,6 @@ CNetServerWorker::~CNetServerWorker()
|
|||
if (m_UPnPThread.joinable())
|
||||
m_UPnPThread.detach();
|
||||
#endif
|
||||
|
||||
// Clean up resources
|
||||
for (const auto& session : m_Sessions)
|
||||
session->DisconnectNow(NDR_SERVER_SHUTDOWN);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -322,21 +569,17 @@ void CNetServerWorker::SetupUPnP(const u16 port)
|
|||
}
|
||||
#endif // CONFIG2_MINIUPNPC
|
||||
|
||||
bool CNetServerWorker::SendMessage(ENetPeer* peer, const CNetMessage* message)
|
||||
bool CNetServerWorker::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
ENSURE(m_Host);
|
||||
m_Sessions.front()->SendMessage(message);
|
||||
|
||||
CNetServerSession* session = static_cast<CNetServerSession*>(peer->data);
|
||||
|
||||
return CNetHost::SendMessage(message, peer, DebugName(session).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetServerWorker::Multicast(const CNetMessage* message,
|
||||
const std::vector<NetServerSessionState>& targetStates,
|
||||
const std::optional<std::vector<std::string>>& receivers /* = std::nullopt */)
|
||||
{
|
||||
ENSURE(m_Host);
|
||||
|
||||
const auto isReceiver = [&](const CNetServerSession& session)
|
||||
{
|
||||
if (!PS::contains(targetStates,
|
||||
|
|
@ -361,14 +604,14 @@ bool CNetServerWorker::Multicast(const CNetMessage* message,
|
|||
return ok;
|
||||
}
|
||||
|
||||
void CNetServerWorker::RunThread(CNetServerWorker* data, const std::string& initAttributes)
|
||||
void CNetServerWorker::RunThread(CNetServerWorker* data, const std::string& initAttributes, u16 port)
|
||||
{
|
||||
debug_SetThreadName("NetServer");
|
||||
|
||||
data->Run(initAttributes);
|
||||
data->Run(initAttributes, port);
|
||||
}
|
||||
|
||||
void CNetServerWorker::Run(const std::string& initAttributes)
|
||||
void CNetServerWorker::Run(const std::string& initAttributes, u16 port)
|
||||
{
|
||||
// The script context uses the profiler and therefore the thread must be registered before the context is created
|
||||
g_Profiler2.RegisterCurrentThread("Net server");
|
||||
|
|
@ -386,22 +629,27 @@ void CNetServerWorker::Run(const std::string& initAttributes)
|
|||
m_InitAttributes = gameAttributesVal;
|
||||
}
|
||||
|
||||
Quic quic{port};
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!RunStep())
|
||||
if (!RunStep(quic))
|
||||
break;
|
||||
|
||||
// Update profiler stats
|
||||
m_Stats->LatchHostState(*m_Host);
|
||||
m_Stats->LatchHostState(m_Sessions);
|
||||
}
|
||||
|
||||
// Clear roots before deleting their context
|
||||
m_SavedCommands.clear();
|
||||
|
||||
SAFE_DELETE(m_ScriptInterface);
|
||||
|
||||
for (const auto& session : m_Sessions)
|
||||
session->Disconnect(NDR_SERVER_SHUTDOWN);
|
||||
}
|
||||
|
||||
bool CNetServerWorker::RunStep()
|
||||
bool CNetServerWorker::RunStep(Quic& quic)
|
||||
{
|
||||
// Check for messages from the game thread.
|
||||
// (Do as little work as possible while the mutex is held open,
|
||||
|
|
@ -442,103 +690,40 @@ bool CNetServerWorker::RunStep()
|
|||
|
||||
CheckClientConnections();
|
||||
|
||||
// Process network events:
|
||||
pollfd pollFd{
|
||||
.fd{quic.m_SocketFd},
|
||||
.events{EPOLLIN | EPOLLOUT}
|
||||
};
|
||||
const int ready{poll(&pollFd, 1, 25)};
|
||||
|
||||
ENetEvent event;
|
||||
int status = enet_host_service(m_Host.get(), &event, HOST_SERVICE_TIMEOUT);
|
||||
if (status < 0)
|
||||
if (ready < 0)
|
||||
throw std::runtime_error{fmt::format("epoll_wait: {}", std::strerror(errno))};
|
||||
|
||||
if (ready == 0)
|
||||
{
|
||||
LOGERROR("CNetServerWorker: enet_host_service failed (%d)", status);
|
||||
// TODO: notify game that the server has shut down
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
// Reached timeout with no events - try again
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process the event:
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
{
|
||||
// Report the client address
|
||||
char hostname[256] = "(error)";
|
||||
enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
|
||||
LOGMESSAGE("Net server: Received connection from %s:%u", hostname, (unsigned int)event.peer->address.port);
|
||||
|
||||
// Set up a session object for this peer
|
||||
|
||||
const std::unique_ptr<CNetServerSession>& session{m_Sessions.emplace_back(
|
||||
std::make_unique<CNetServerSession>(*this, event.peer))};
|
||||
|
||||
SetupSession(session.get());
|
||||
|
||||
ENSURE(event.peer->data == NULL);
|
||||
event.peer->data = session.get();
|
||||
|
||||
HandleConnect(session.get());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
{
|
||||
// If there is an active session with this peer, then reset and delete it
|
||||
|
||||
CNetServerSession* session = static_cast<CNetServerSession*>(event.peer->data);
|
||||
if (session)
|
||||
for (auto& session : m_Sessions)
|
||||
{
|
||||
LOGMESSAGE("Net server: Disconnected %s", DebugName(session).c_str());
|
||||
|
||||
// Remove the session first, so we won't send player-update messages to it
|
||||
// when updating the FSM
|
||||
const auto iter = std::ranges::find(m_Sessions, session,
|
||||
&std::unique_ptr<CNetServerSession>::get);
|
||||
const std::unique_ptr<CNetServerSession> _ = std::move(*iter);
|
||||
m_Sessions.erase(iter);
|
||||
|
||||
session->Update((uint)NMT_CONNECTION_LOST, NULL);
|
||||
|
||||
event.peer->data = NULL;
|
||||
}
|
||||
|
||||
if (m_State == SERVER_STATE_LOADING)
|
||||
CheckGameLoadStatus(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
{
|
||||
// If there is an active session with this peer, then process the message
|
||||
|
||||
CNetServerSession* session = static_cast<CNetServerSession*>(event.peer->data);
|
||||
if (session)
|
||||
{
|
||||
// Create message from raw data
|
||||
CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, GetScriptInterface());
|
||||
if (msg)
|
||||
ngtcp2_conn *conn = session->m_Connection.m_QuicConnection.get();
|
||||
const int ret{ngtcp2_conn_handle_expiry(conn, timestamp())};
|
||||
if (ret < 0)
|
||||
{
|
||||
LOGMESSAGE("Net server: Received message %s of size %lu from %s", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength(), DebugName(session).c_str());
|
||||
|
||||
HandleMessageReceive(msg, session);
|
||||
|
||||
delete msg;
|
||||
LOGERROR("ngtcp2_conn_handle_expiry: %s", ngtcp2_strerror(ret));
|
||||
continue;
|
||||
}
|
||||
|
||||
session->m_Connection.Write(quic.m_SocketFd);
|
||||
}
|
||||
|
||||
// Done using the packet
|
||||
enet_packet_destroy(event.packet);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pollFd.revents & EPOLLIN)
|
||||
quic.HandleIncoming(*this);
|
||||
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
if (pollFd.revents & EPOLLOUT)
|
||||
{
|
||||
for (auto& session : m_Sessions)
|
||||
session->m_Connection.Write(quic.m_SocketFd);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -1644,10 +1829,8 @@ CStrW CNetServerWorker::DeduplicatePlayerName(const CStrW& original)
|
|||
}
|
||||
}
|
||||
|
||||
void CNetServerWorker::SendHolePunchingMessage(const CStr& ipStr, u16 port)
|
||||
void CNetServerWorker::SendHolePunchingMessage(const CStr& /*ipStr*/, u16 /*port*/)
|
||||
{
|
||||
if (m_Host)
|
||||
StunClient::SendHolePunchingMessages(*m_Host, ipStr, port);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1665,9 +1848,9 @@ CNetServer::CNetServer(const bool continueSavedGame, std::uint16_t port, const b
|
|||
|
||||
// In lobby, we send our public ip and port on request to the players who want to connect.
|
||||
// Thus we need to know our public IP and use STUN to get it.
|
||||
std::lock_guard<std::mutex> lock(m_Worker.m_WorkerMutex);
|
||||
if (!m_Worker.m_Host || !StunClient::FindPublicIP(*m_Worker.m_Host, m_PublicIp, m_PublicPort))
|
||||
throw std::runtime_error{"Failed to resolve public IP-address."};
|
||||
// std::lock_guard<std::mutex> lock(m_Worker.m_WorkerMutex);
|
||||
// if (!m_Worker.m_Host || !StunClient::FindPublicIP(*m_Worker.m_Host, m_PublicIp, m_PublicPort))
|
||||
// throw std::runtime_error{"Failed to resolve public IP-address."};
|
||||
}
|
||||
|
||||
bool CNetServer::UseLobbyAuth() const
|
||||
|
|
@ -1688,9 +1871,7 @@ u16 CNetServer::GetPublicPort() const
|
|||
u16 CNetServer::GetLocalPort() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Worker.m_WorkerMutex);
|
||||
if (!m_Worker.m_Host)
|
||||
return 0;
|
||||
return m_Worker.m_Host->address.port;
|
||||
return 0; // m_Worker.m_Host->address.port;
|
||||
}
|
||||
|
||||
bool CNetServer::CheckPasswordAndIncrement(const std::string& username, const std::string& password, const std::string& salt)
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public:
|
|||
/**
|
||||
* Send a message to the given network peer.
|
||||
*/
|
||||
bool SendMessage(ENetPeer* peer, const CNetMessage* message);
|
||||
bool SendMessage(const CNetMessage* message);
|
||||
|
||||
/**
|
||||
* Disconnects a player from gamesetup or session.
|
||||
|
|
@ -173,10 +173,12 @@ private:
|
|||
*/
|
||||
CStrW DeduplicatePlayerName(const CStrW& original);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the script context used for init attributes.
|
||||
*/
|
||||
const ScriptInterface& GetScriptInterface();
|
||||
private:
|
||||
|
||||
/**
|
||||
* Set the turn length to a fixed value.
|
||||
|
|
@ -227,7 +229,9 @@ private:
|
|||
|
||||
void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
|
||||
|
||||
public:
|
||||
void HandleMessageReceive(CNetMessage* message, CNetServerSession* session);
|
||||
public:
|
||||
|
||||
/**
|
||||
* Send a network warning if the connection to a client is being lost or has bad latency.
|
||||
|
|
@ -263,7 +267,8 @@ private:
|
|||
*/
|
||||
const bool m_LobbyAuth;
|
||||
|
||||
std::unique_ptr<ENetHost, DestroyHost> m_Host;
|
||||
class Quic;
|
||||
|
||||
std::vector<std::unique_ptr<CNetServerSession>> m_Sessions;
|
||||
|
||||
std::unique_ptr<CNetStatsTable> m_Stats;
|
||||
|
|
@ -330,9 +335,9 @@ private:
|
|||
std::thread m_UPnPThread;
|
||||
#endif
|
||||
|
||||
static void RunThread(CNetServerWorker* data, const std::string& initAttributes);
|
||||
void Run(const std::string& initAttributes);
|
||||
bool RunStep();
|
||||
static void RunThread(CNetServerWorker* data, const std::string& initAttributes, u16 port);
|
||||
void Run(const std::string& initAttributes, u16 port);
|
||||
bool RunStep(Quic& quic);
|
||||
|
||||
std::thread m_WorkerThread;
|
||||
mutable std::mutex m_WorkerMutex;
|
||||
|
|
|
|||
|
|
@ -25,8 +25,312 @@
|
|||
#include "network/NetServer.h"
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
|
||||
m_Server(server), m_FileTransferer(*this), m_Peer(peer)
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
void SendPacket(const int socketFd, const std::span<const std::uint8_t> data, const ngtcp2_addr remote)
|
||||
{
|
||||
iovec iov{
|
||||
.iov_base{const_cast<std::uint8_t*>(data.data())},
|
||||
.iov_len{data.size()}
|
||||
};
|
||||
|
||||
msghdr msg{};
|
||||
msg.msg_name = remote.addr;
|
||||
msg.msg_namelen = remote.addrlen;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
ssize_t ret;
|
||||
do
|
||||
ret = sendmsg(socketFd, &msg, MSG_DONTWAIT);
|
||||
while (ret < 0 && errno == EINTR);
|
||||
if (ret < 0)
|
||||
throw std::system_error{errno, std::generic_category(), "Error sending message"};
|
||||
}
|
||||
|
||||
ngtcp2_conn* GetConnection(ngtcp2_crypto_conn_ref* connRef)
|
||||
{
|
||||
return reinterpret_cast<std::unique_ptr<ngtcp2_conn, ConnectionDeleter>*>(connRef->user_data)->get();
|
||||
}
|
||||
|
||||
int OnReceiveStreamData(ngtcp2_conn* conn, const std::uint32_t /*flags*/, const std::int64_t streamId,
|
||||
const std::uint64_t /*offset*/, const std::uint8_t* data, const std::size_t datalen, void* userData,
|
||||
void* /*streamUserData*/)
|
||||
{
|
||||
CNetServerSession& session{*static_cast<CNetServerSession*>(userData)};
|
||||
const auto messageData = session.m_Connection.m_Stream.value().Receive({data, datalen});
|
||||
if (messageData.has_value())
|
||||
{
|
||||
std::unique_ptr<CNetMessage> message{CNetMessageFactory::CreateMessage(messageData.value(),
|
||||
session.GetServer().GetScriptInterface())};
|
||||
session.GetServer().HandleMessageReceive(message.get(), &session);
|
||||
}
|
||||
increaseWindow(conn, streamId, datalen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OnAcknowledgedStreamData(ngtcp2_conn*, const std::int64_t, const std::uint64_t offset,
|
||||
const std::uint64_t dataLength, void* userData, void*)
|
||||
{
|
||||
Connection& connection{static_cast<CNetServerSession*>(userData)->m_Connection};
|
||||
Stream& stream{connection.GetStream()};
|
||||
stream.MarkAcknowledged(offset + dataLength);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OnConnect(ngtcp2_conn*, const ngtcp2_encryption_level level, void* userData)
|
||||
{
|
||||
if (level != NGTCP2_ENCRYPTION_LEVEL_1RTT)
|
||||
return 0;
|
||||
Connection& connection{static_cast<CNetServerSession*>(userData)->m_Connection};
|
||||
try
|
||||
{
|
||||
connection.OpenStream();
|
||||
}
|
||||
catch (const std::runtime_error&)
|
||||
{
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
const CSrvHandshakeMessage handshake(CreateHandshake<CSrvHandshakeMessage>());
|
||||
connection.GetStream().PushMessage(&handshake);
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr ngtcp2_callbacks callbacks{
|
||||
.recv_client_initial{&ngtcp2_crypto_recv_client_initial_cb},
|
||||
.recv_crypto_data{&ngtcp2_crypto_recv_crypto_data_cb},
|
||||
.encrypt{&ngtcp2_crypto_encrypt_cb},
|
||||
.decrypt{&ngtcp2_crypto_decrypt_cb},
|
||||
.hp_mask{&ngtcp2_crypto_hp_mask_cb},
|
||||
.recv_stream_data{&OnReceiveStreamData},
|
||||
.acked_stream_data_offset{&OnAcknowledgedStreamData},
|
||||
.recv_retry{&ngtcp2_crypto_recv_retry_cb},
|
||||
.rand{&OnRandomRequest},
|
||||
.get_new_connection_id{&OnNewConnectionIdRequest},
|
||||
.update_key{&ngtcp2_crypto_update_key_cb},
|
||||
.delete_crypto_aead_ctx{&ngtcp2_crypto_delete_crypto_aead_ctx_cb},
|
||||
.delete_crypto_cipher_ctx{&ngtcp2_crypto_delete_crypto_cipher_ctx_cb},
|
||||
.get_path_challenge_data{&ngtcp2_crypto_get_path_challenge_data_cb},
|
||||
.recv_tx_key{&OnConnect}
|
||||
};
|
||||
|
||||
void WriteToStream(const int socketFd, ngtcp2_conn* conn, Stream* stream, const ngtcp2_addr remote)
|
||||
{
|
||||
std::array<std::uint8_t, MAX_UDP_PAYLOAD_SIZE> buf;
|
||||
|
||||
ngtcp2_path_storage ps;
|
||||
ngtcp2_path_storage_zero(&ps);
|
||||
|
||||
ngtcp2_pkt_info pi;
|
||||
const std::uint64_t ts{timestamp()};
|
||||
|
||||
std::uint32_t flags{NGTCP2_WRITE_STREAM_FLAG_MORE};
|
||||
|
||||
while (true)
|
||||
{
|
||||
ngtcp2_vec datav;
|
||||
std::int64_t stream_id;
|
||||
|
||||
if (stream)
|
||||
{
|
||||
auto bytesToSend = stream->PeekData();
|
||||
if (bytesToSend.has_value())
|
||||
{
|
||||
datav.base = const_cast<uint8_t*>(bytesToSend->data());
|
||||
datav.len = bytesToSend->size();
|
||||
stream_id = stream->m_Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No stream data to be sent */
|
||||
datav.base = nullptr;
|
||||
datav.len = 0;
|
||||
stream_id = -1;
|
||||
flags &= ~NGTCP2_WRITE_STREAM_FLAG_MORE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
datav.base = NULL;
|
||||
datav.len = 0;
|
||||
stream_id = -1;
|
||||
}
|
||||
|
||||
ngtcp2_ssize n_read;
|
||||
const ngtcp2_ssize n_written{ngtcp2_conn_writev_stream(conn, &ps.path, &pi, buf.data(),
|
||||
buf.size(), &n_read, flags, stream_id, &datav, 1, ts)};
|
||||
if (n_written < 0)
|
||||
{
|
||||
if (n_written != NGTCP2_ERR_WRITE_MORE)
|
||||
{
|
||||
throw std::runtime_error{fmt::format("ngtcp2_conn_writev_stream: {}",
|
||||
ngtcp2_strerror(static_cast<int>(n_written)))};
|
||||
}
|
||||
if (stream && n_read > 0)
|
||||
stream->MarkSent(n_read);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n_written == 0)
|
||||
break;
|
||||
|
||||
if (stream && n_read > 0)
|
||||
stream->MarkSent(n_read);
|
||||
|
||||
try
|
||||
{
|
||||
SendPacket(socketFd, {buf.data(), static_cast<std::size_t>(n_written)}, remote);
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
if (e.code().value() == EAGAIN || e.code().value() == EWOULDBLOCK)
|
||||
break;
|
||||
throw;
|
||||
}
|
||||
|
||||
/* No stream data to be sent */
|
||||
if (stream && datav.len == 0)
|
||||
break;
|
||||
}
|
||||
ngtcp2_conn_update_pkt_tx_time(conn, timestamp());
|
||||
}
|
||||
|
||||
std::unique_ptr<gnutls_session_int, SessionDeleter> createTlsSession(
|
||||
const gnutls_certificate_credentials_t cred)
|
||||
{
|
||||
gnutls_session_t tempSession;
|
||||
if (const int ret{gnutls_init(&tempSession, GNUTLS_SERVER | GNUTLS_ENABLE_EARLY_DATA |
|
||||
GNUTLS_NO_END_OF_EARLY_DATA)})
|
||||
{
|
||||
throw std::runtime_error{fmt::format("gnutls_init: {}", gnutls_strerror(ret))};
|
||||
}
|
||||
std::unique_ptr<gnutls_session_int, SessionDeleter> session{tempSession};
|
||||
|
||||
if (const int ret{gnutls_priority_set_direct(session.get(), TLS_PRIORITY, NULL)})
|
||||
throw std::runtime_error{fmt::format("gnutls_priority_set_direct: {}", gnutls_strerror(ret))};
|
||||
|
||||
if (const int ret{gnutls_credentials_set(session.get(), GNUTLS_CRD_CERTIFICATE, cred)})
|
||||
throw std::runtime_error{fmt::format("gnutls_credentials_set: {}", gnutls_strerror(ret))};
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
Connection::Connection(CNetServerSession& session, const ngtcp2_settings& settings,
|
||||
gnutls_certificate_credentials_t credentials, const ngtcp2_pkt_hd& header,
|
||||
const ngtcp2_cid& newScid, const ngtcp2_path& path):
|
||||
m_TlsSession{createTlsSession(credentials)},
|
||||
m_ConnRef{
|
||||
.get_conn{&GetConnection},
|
||||
.user_data{&m_QuicConnection}
|
||||
}
|
||||
{
|
||||
ngtcp2_transport_params params;
|
||||
ngtcp2_transport_params_default(¶ms);
|
||||
params.initial_max_stream_data_bidi_local = 128 * KiB;
|
||||
params.initial_max_data = 1 * MiB;
|
||||
params.max_udp_payload_size = MAX_UDP_PAYLOAD_SIZE;
|
||||
ngtcp2_cid_init(¶ms.original_dcid, header.dcid.data, header.dcid.datalen);
|
||||
params.original_dcid_present = true;
|
||||
params.grease_quic_bit = 1;
|
||||
|
||||
ngtcp2_conn* tempConn;
|
||||
if (const int ret = ngtcp2_conn_server_new(&tempConn, &header.scid, &newScid, &path, header.version,
|
||||
&callbacks, &settings, ¶ms, nullptr, &session))
|
||||
{
|
||||
throw std::runtime_error{fmt::format("ngtcp2_conn_server_new: {}",
|
||||
ngtcp2_strerror (ret))};
|
||||
}
|
||||
|
||||
m_QuicConnection.reset(tempConn);
|
||||
|
||||
memcpy(&m_LocalAddress, path.local.addr, path.local.addrlen);
|
||||
m_LocalAddressLength = path.local.addrlen;
|
||||
memcpy(&m_RemoteAddress, path.remote.addr, path.remote.addrlen);
|
||||
m_RemoteAddressLength = path.remote.addrlen;
|
||||
|
||||
ngtcp2_crypto_gnutls_configure_server_session(m_TlsSession.get());
|
||||
ngtcp2_conn_set_tls_native_handle(m_QuicConnection.get(), m_TlsSession.get());
|
||||
gnutls_session_set_ptr(m_TlsSession.get(), &m_ConnRef);
|
||||
m_TimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
||||
if (m_TimerFd < 0)
|
||||
throw std::system_error(errno, std::generic_category(), "timerfd_create");
|
||||
}
|
||||
|
||||
void Connection::OpenStream()
|
||||
{
|
||||
std::int64_t streamId;
|
||||
if (ngtcp2_conn_open_bidi_stream(m_QuicConnection.get(), &streamId, nullptr))
|
||||
throw std::runtime_error{""};
|
||||
m_Stream.emplace(streamId);
|
||||
}
|
||||
|
||||
Stream& Connection::GetStream()
|
||||
{
|
||||
return m_Stream.value();
|
||||
}
|
||||
|
||||
void Connection::Read(const ngtcp2_addr remote, const std::span<std::uint8_t> data)
|
||||
{
|
||||
const ngtcp2_path path{
|
||||
.local{ngtcp2_conn_get_path(m_QuicConnection.get())->local},
|
||||
.remote{remote}
|
||||
};
|
||||
|
||||
ngtcp2_pkt_info pi{};
|
||||
if (const int ret{ngtcp2_conn_read_pkt(m_QuicConnection.get(), &path, &pi, data.data(), data.size(),
|
||||
timestamp())})
|
||||
{
|
||||
throw std::runtime_error{"Destroy connection"};
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::Write(const int socketFd)
|
||||
{
|
||||
WriteToStream(socketFd, m_QuicConnection.get(), nullptr,
|
||||
{&m_RemoteAddress.sa, m_RemoteAddressLength});
|
||||
|
||||
if (m_Stream.has_value())
|
||||
{
|
||||
WriteToStream(socketFd, m_QuicConnection.get(), &m_Stream.value(),
|
||||
{&m_RemoteAddress.sa, m_RemoteAddressLength});
|
||||
}
|
||||
|
||||
const ngtcp2_tstamp expiry{ngtcp2_conn_get_expiry(m_QuicConnection.get())};
|
||||
const ngtcp2_tstamp now{timestamp()};
|
||||
itimerspec it{};
|
||||
|
||||
if (const int ret{timerfd_settime(m_TimerFd, 0, &it, nullptr)})
|
||||
throw std::system_error{errno, std::generic_category(), "timerfd_settime"};
|
||||
if (expiry < now)
|
||||
{
|
||||
it.it_value.tv_sec = 0;
|
||||
it.it_value.tv_nsec = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
it.it_value.tv_sec = (expiry - now) / NGTCP2_SECONDS;
|
||||
it.it_value.tv_nsec = ((expiry - now) % NGTCP2_SECONDS) / NGTCP2_NANOSECONDS;
|
||||
}
|
||||
|
||||
if (const int ret{timerfd_settime(m_TimerFd, 0, &it, nullptr)})
|
||||
throw std::system_error{errno, std::generic_category(), "timerfd_settime"};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CNetServerSession::CNetServerSession(CNetServerWorker& server, const int socketFd,
|
||||
const ngtcp2_settings& settings, gnutls_certificate_credentials_t credentials,
|
||||
const ngtcp2_pkt_hd& header, const ngtcp2_cid& newScid, const ngtcp2_path& path) :
|
||||
m_Server(server),
|
||||
m_Connection{*this, settings, credentials, header, newScid, path},
|
||||
m_FileTransferer(*this),
|
||||
m_SocketFd{socketFd}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -58,18 +362,30 @@ void CNetServerSession::Disconnect(NetDisconnectReason reason)
|
|||
|
||||
Update((uint)NMT_CONNECTION_LOST, NULL);
|
||||
|
||||
enet_peer_disconnect(m_Peer, static_cast<enet_uint32>(reason));
|
||||
}
|
||||
const ngtcp2_ccerr quicReason{
|
||||
.type{NGTCP2_CCERR_TYPE_APPLICATION},
|
||||
.error_code{reason}
|
||||
};
|
||||
|
||||
void CNetServerSession::DisconnectNow(NetDisconnectReason reason)
|
||||
{
|
||||
if (reason == NDR_UNKNOWN)
|
||||
LOGWARNING("Disconnecting client without communicating the disconnect reason!");
|
||||
ngtcp2_sockaddr_union local;
|
||||
ngtcp2_sockaddr_union remote;
|
||||
ngtcp2_path path{
|
||||
.local{.addr{&local.sa}},
|
||||
.remote{.addr{&remote.sa}}
|
||||
};
|
||||
std::array<std::uint8_t, MAX_UDP_PAYLOAD_SIZE> buffer;
|
||||
const ngtcp2_ssize amount{ngtcp2_conn_write_connection_close(m_Connection.m_QuicConnection.get(), &path, nullptr, buffer.data(),
|
||||
buffer.size(), &quicReason, timestamp())};
|
||||
if (amount <= 0)
|
||||
LOGERROR("closing connection %s", ngtcp2_strerror(static_cast<int>(amount)));
|
||||
|
||||
enet_peer_disconnect_now(m_Peer, static_cast<enet_uint32>(reason));
|
||||
SendPacket(m_SocketFd, {buffer.data(), static_cast<std::size_t>(amount)}, path.remote);
|
||||
}
|
||||
|
||||
bool CNetServerSession::SendMessage(const CNetMessage* message)
|
||||
{
|
||||
return m_Server.SendMessage(m_Peer, message);
|
||||
m_Connection.m_Stream.value().PushMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,51 @@
|
|||
#include "network/NetHost.h"
|
||||
#include "ps/CStr.h"
|
||||
|
||||
#include <deque>
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <ngtcp2/ngtcp2_crypto.h>
|
||||
#include <ngtcp2/ngtcp2_crypto_gnutls.h>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include "network/NetProtocol.h"
|
||||
#include "ps/CLogger.h"
|
||||
|
||||
class CNetServerWorker;
|
||||
|
||||
typedef struct _ENetPeer ENetPeer;
|
||||
|
||||
class CNetServerSession;
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
Connection(CNetServerSession& session, const ngtcp2_settings& settings,
|
||||
gnutls_certificate_credentials_t credentials, const ngtcp2_pkt_hd& header,
|
||||
const ngtcp2_cid& scid, const ngtcp2_path& path);
|
||||
|
||||
void OpenStream();
|
||||
Stream& GetStream();
|
||||
|
||||
void Read(const ngtcp2_addr remote, const std::span<std::uint8_t> data);
|
||||
void Write(const int socketFd);
|
||||
|
||||
std::unique_ptr<gnutls_session_int, SessionDeleter> m_TlsSession;
|
||||
std::unique_ptr<ngtcp2_conn, ConnectionDeleter> m_QuicConnection;
|
||||
int m_TimerFd{-1};
|
||||
public:
|
||||
ngtcp2_sockaddr_union m_LocalAddress;
|
||||
ngtcp2_socklen m_LocalAddressLength;
|
||||
ngtcp2_sockaddr_union m_RemoteAddress;
|
||||
ngtcp2_socklen m_RemoteAddressLength;
|
||||
std::optional<Stream> m_Stream;
|
||||
ngtcp2_crypto_conn_ref m_ConnRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* The server's end of a network session.
|
||||
* Represents an abstraction of the state of the client, storing all the per-client data
|
||||
|
|
@ -40,7 +81,9 @@ class CNetServerSession : public CFsm<CNetServerSession, CNetMessage*>
|
|||
NONCOPYABLE(CNetServerSession);
|
||||
|
||||
public:
|
||||
CNetServerSession(CNetServerWorker& server, ENetPeer* peer);
|
||||
CNetServerSession(CNetServerWorker& server, const int socketFd, const ngtcp2_settings& settings,
|
||||
gnutls_certificate_credentials_t credentials, const ngtcp2_pkt_hd& header,
|
||||
const ngtcp2_cid& newScid, const ngtcp2_path& path);
|
||||
|
||||
CNetServerWorker& GetServer() { return m_Server; }
|
||||
|
||||
|
|
@ -73,13 +116,6 @@ public:
|
|||
*/
|
||||
void Disconnect(NetDisconnectReason reason);
|
||||
|
||||
/**
|
||||
* Sends an unreliable disconnection notification to the client.
|
||||
* The server will not receive any disconnection notification.
|
||||
* The server will not receive any further messages sent via this session.
|
||||
*/
|
||||
void DisconnectNow(NetDisconnectReason reason);
|
||||
|
||||
/**
|
||||
* Send a message to the client.
|
||||
*/
|
||||
|
|
@ -89,7 +125,10 @@ public:
|
|||
|
||||
private:
|
||||
CNetServerWorker& m_Server;
|
||||
public:
|
||||
Connection m_Connection;
|
||||
|
||||
private:
|
||||
CNetFileTransferer m_FileTransferer;
|
||||
|
||||
ENetPeer* m_Peer;
|
||||
|
|
@ -98,6 +137,7 @@ private:
|
|||
CStrW m_UserName;
|
||||
u32 m_HostID{0};
|
||||
CStr m_Password;
|
||||
int m_SocketFd;
|
||||
};
|
||||
|
||||
#endif // NET_SERVER_SESSION_H
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
#include "NetStats.h"
|
||||
|
||||
#include "network/NetServerSession.h"
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <string>
|
||||
|
||||
enum
|
||||
|
|
@ -37,13 +40,8 @@ enum
|
|||
NumberRows
|
||||
};
|
||||
|
||||
CNetStatsTable::CNetStatsTable(const ENetPeer& peer)
|
||||
: m_Peer(&peer)
|
||||
{
|
||||
}
|
||||
|
||||
CNetStatsTable::CNetStatsTable()
|
||||
: m_Peer(NULL)
|
||||
CNetStatsTable::CNetStatsTable(ngtcp2_conn* conn):
|
||||
m_Connection{conn}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +52,7 @@ CStr CNetStatsTable::GetName()
|
|||
|
||||
CStr CNetStatsTable::GetTitle()
|
||||
{
|
||||
if (m_Peer)
|
||||
if (m_Connection)
|
||||
return "Network client statistics";
|
||||
else
|
||||
return "Network host statistics";
|
||||
|
|
@ -70,7 +68,7 @@ const std::vector<ProfileColumn>& CNetStatsTable::GetColumns()
|
|||
m_ColumnDescriptions.clear();
|
||||
m_ColumnDescriptions.push_back(ProfileColumn("Name", 200));
|
||||
|
||||
if (m_Peer)
|
||||
if (m_Connection)
|
||||
m_ColumnDescriptions.push_back(ProfileColumn("Value", 80));
|
||||
else
|
||||
{
|
||||
|
|
@ -95,22 +93,26 @@ CStr CNetStatsTable::GetCellText(size_t row, size_t col)
|
|||
#define ROW(id, title, member) \
|
||||
case id: \
|
||||
if (col == 0) return title; \
|
||||
if (m_Peer) return CStr::FromUInt(m_Peer->member); \
|
||||
if (m_Connection) return member; \
|
||||
return "???"
|
||||
|
||||
ngtcp2_conn_info info;
|
||||
if (col != 0)
|
||||
ngtcp2_conn_get_conn_info(m_Connection, &info);
|
||||
|
||||
switch(row)
|
||||
{
|
||||
ROW(Row_InData, "incoming bytes", incomingDataTotal);
|
||||
ROW(Row_OutData, "outgoing bytes", outgoingDataTotal);
|
||||
ROW(Row_LastSendTime, "last send time", lastSendTime);
|
||||
ROW(Row_LastRecvTime, "last receive time", lastReceiveTime);
|
||||
ROW(Row_NextTimeout, "next timeout", nextTimeout);
|
||||
ROW(Row_PacketsSent, "packets sent", packetsSent);
|
||||
ROW(Row_PacketsLost, "packets lost", packetsLost);
|
||||
ROW(Row_LastRTT, "last RTT", lastRoundTripTime);
|
||||
ROW(Row_RTT, "mean RTT", roundTripTime);
|
||||
ROW(Row_MTU, "MTU", mtu);
|
||||
ROW(Row_ReliableInTransit, "reliable data in transit", reliableDataInTransit);
|
||||
ROW(Row_InData, "incoming bytes", {});
|
||||
ROW(Row_OutData, "outgoing bytes", {});
|
||||
ROW(Row_LastSendTime, "last send time", {});
|
||||
ROW(Row_LastRecvTime, "last receive time", {});
|
||||
ROW(Row_NextTimeout, "next timeout", CStr::FromUInt(ngtcp2_conn_get_expiry(m_Connection)));
|
||||
ROW(Row_PacketsSent, "packets sent", {});
|
||||
ROW(Row_PacketsLost, "packets lost", {});
|
||||
ROW(Row_LastRTT, "last RTT", CStr::FromUInt(info.latest_rtt));
|
||||
ROW(Row_RTT, "mean RTT", CStr::FromUInt(info.smoothed_rtt));
|
||||
ROW(Row_MTU, "MTU", CStr::FromUInt(ngtcp2_conn_get_path_max_tx_udp_payload_size(m_Connection)));
|
||||
ROW(Row_ReliableInTransit, "reliable data in transit", CStr::FromUInt(info.bytes_in_flight));
|
||||
|
||||
default:
|
||||
return "???";
|
||||
|
|
@ -124,29 +126,39 @@ AbstractProfileTable* CNetStatsTable::GetChild(size_t /*row*/)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void CNetStatsTable::LatchHostState(const ENetHost& host)
|
||||
void CNetStatsTable::LatchHostState(const std::span<std::unique_ptr<CNetServerSession>> sessions)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
|
||||
#define ROW(id, title, member) \
|
||||
m_LatchedData[i].push_back(CStr::FromUInt(host.peers[i].member));
|
||||
m_LatchedData[i].push_back(CStr::FromUInt(info.member));
|
||||
|
||||
m_LatchedData.clear();
|
||||
m_LatchedData.resize(host.peerCount);
|
||||
m_LatchedData.resize(sessions.size());
|
||||
|
||||
for (size_t i = 0; i < host.peerCount; ++i)
|
||||
for (size_t i = 0; i < sessions.size(); ++i)
|
||||
{
|
||||
ROW(Row_InData, "incoming bytes", incomingDataTotal);
|
||||
ROW(Row_OutData, "outgoing bytes", outgoingDataTotal);
|
||||
ROW(Row_LastSendTime, "last send time", lastSendTime);
|
||||
ROW(Row_LastRecvTime, "last receive time", lastReceiveTime);
|
||||
ROW(Row_NextTimeout, "next timeout", nextTimeout);
|
||||
ROW(Row_PacketsSent, "packets sent", packetsSent);
|
||||
ROW(Row_PacketsLost, "packets lost", packetsLost);
|
||||
ROW(Row_LastRTT, "last RTT", lastRoundTripTime);
|
||||
ROW(Row_RTT, "mean RTT", roundTripTime);
|
||||
ROW(Row_MTU, "MTU", mtu);
|
||||
ROW(Row_ReliableInTransit, "reliable data in transit", reliableDataInTransit);
|
||||
ngtcp2_conn_info info;
|
||||
ngtcp2_conn_get_conn_info(sessions[i]->m_Connection.m_QuicConnection.get(), &info);
|
||||
// ROW(Row_InData, "incoming bytes", bytes_recv);
|
||||
m_LatchedData[i].push_back({});
|
||||
// ROW(Row_OutData, "outgoing bytes", bytes_sent);
|
||||
m_LatchedData[i].push_back({});
|
||||
// ROW(Row_LastSendTime, "last send time", lastSendTime);
|
||||
m_LatchedData[i].push_back({});
|
||||
// ROW(Row_LastRecvTime, "last receive time", lastReceiveTime);
|
||||
m_LatchedData[i].push_back({});
|
||||
m_LatchedData[i].push_back(CStr::FromUInt(ngtcp2_conn_get_expiry(
|
||||
sessions[i]->m_Connection.m_QuicConnection.get())));
|
||||
// ROW(Row_PacketsSent, "packets sent", pkt_sent);
|
||||
m_LatchedData[i].push_back({});
|
||||
// ROW(Row_PacketsLost, "packets lost", pkt_lost);
|
||||
m_LatchedData[i].push_back({});
|
||||
ROW(Row_LastRTT, "last RTT", latest_rtt);
|
||||
ROW(Row_RTT, "mean RTT", smoothed_rtt);
|
||||
m_LatchedData[i].push_back(CStr::FromUInt(ngtcp2_conn_get_path_max_tx_udp_payload_size(
|
||||
sessions[i]->m_Connection.m_QuicConnection.get())));
|
||||
ROW(Row_ReliableInTransit, "reliable data in transit", bytes_in_flight);
|
||||
}
|
||||
#undef ROW
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,13 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
typedef struct _ENetPeer ENetPeer;
|
||||
typedef struct _ENetHost ENetHost;
|
||||
|
||||
class CNetServerSession;
|
||||
struct ngtcp2_conn;
|
||||
|
||||
/**
|
||||
* ENet connection statistics profiler table.
|
||||
|
|
@ -43,8 +46,8 @@ class CNetStatsTable : public AbstractProfileTable
|
|||
{
|
||||
NONCOPYABLE(CNetStatsTable);
|
||||
public:
|
||||
CNetStatsTable();
|
||||
CNetStatsTable(const ENetPeer& peer);
|
||||
CNetStatsTable() = default;
|
||||
CNetStatsTable(ngtcp2_conn* conn);
|
||||
|
||||
CStr GetName() override;
|
||||
CStr GetTitle() override;
|
||||
|
|
@ -53,10 +56,10 @@ public:
|
|||
CStr GetCellText(size_t row, size_t col) override;
|
||||
AbstractProfileTable* GetChild(size_t row) override;
|
||||
|
||||
void LatchHostState(const ENetHost& host);
|
||||
void LatchHostState(const std::span<std::unique_ptr<CNetServerSession>> sessions);
|
||||
|
||||
private:
|
||||
const ENetPeer* m_Peer;
|
||||
ngtcp2_conn* m_Connection{nullptr};
|
||||
std::vector<ProfileColumn> m_ColumnDescriptions;
|
||||
|
||||
std::mutex m_Mutex;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
TS_ASSERT_EQUALS(msg.Serialize(buf) - (buf+len), 0);
|
||||
TS_ASSERT_EQUALS(buf[len], '!');
|
||||
|
||||
CNetMessage* msg2 = CNetMessageFactory::CreateMessage(buf, len, script);
|
||||
CNetMessage* msg2 = CNetMessageFactory::CreateMessage({buf, len}, script);
|
||||
TS_ASSERT_STR_EQUALS(((CSimulationMessage*)msg2)->ToString(), "CSimulationMessage { m_Client: 1, m_Player: 2, m_Turn: 3, m_Data: [4] }");
|
||||
|
||||
delete msg2;
|
||||
|
|
|
|||
Loading…
Reference in a new issue