test_core_net.odin 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. Copyright 2021 Jeroen van Rijn <[email protected]>.
  3. Made available under Odin's BSD-3 license.
  4. List of contributors:
  5. Jeroen van Rijn: Initial implementation.
  6. graphitemaster: pton/ntop IANA test vectors
  7. A test suite for `core:net`
  8. */
  9. package test_core_net
  10. import "core:testing"
  11. import "core:mem"
  12. import "core:fmt"
  13. import "core:net"
  14. import "core:strconv"
  15. import "core:time"
  16. import "core:thread"
  17. _, _ :: time, thread
  18. TEST_count := 0
  19. TEST_fail := 0
  20. t := &testing.T{}
  21. when ODIN_TEST {
  22. expect :: testing.expect
  23. log :: testing.log
  24. } else {
  25. expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
  26. TEST_count += 1
  27. if !condition {
  28. TEST_fail += 1
  29. fmt.printf("[%v] %v\n", loc, message)
  30. return
  31. }
  32. }
  33. log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
  34. fmt.printf("[%v] ", loc)
  35. fmt.printf("log: %v\n", v)
  36. }
  37. }
  38. _tracking_allocator := mem.Tracking_Allocator{}
  39. print_tracking_allocator_report :: proc() {
  40. for _, leak in _tracking_allocator.allocation_map {
  41. fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
  42. }
  43. for bf in _tracking_allocator.bad_free_array {
  44. fmt.printf("%v allocation %p was freed badly\n", bf.location, bf.memory)
  45. }
  46. }
  47. main :: proc() {
  48. mem.tracking_allocator_init(&_tracking_allocator, context.allocator)
  49. context.allocator = mem.tracking_allocator(&_tracking_allocator)
  50. address_parsing_test(t)
  51. when ODIN_OS != .Windows {
  52. fmt.printf("IMPORTANT: `core:thread` seems to still be a bit wonky on Linux and MacOS, so we can't run tests relying on them.\n", ODIN_OS)
  53. } else {
  54. tcp_tests(t)
  55. }
  56. fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
  57. print_tracking_allocator_report()
  58. }
  59. @test
  60. address_parsing_test :: proc(t: ^testing.T) {
  61. for vector in IP_Address_Parsing_Test_Vectors {
  62. kind := ""
  63. switch vector.family {
  64. case .IP4: kind = "[IPv4]"
  65. case .IP4_Alt: kind = "[IPv4 Non-Decimal]"
  66. case .IP6: kind = "[IPv6]"
  67. case: panic("Add support to the test for this type.")
  68. }
  69. valid := len(vector.binstr) > 0
  70. fmt.printf("%v %v\n", kind, vector.input)
  71. msg := "-set a proper message-"
  72. switch vector.family {
  73. case .IP4, .IP4_Alt:
  74. /*
  75. Does `net.parse_ip4_address` think we parsed the address properly?
  76. */
  77. non_decimal := vector.family == .IP4_Alt
  78. any_addr := net.parse_address(vector.input, non_decimal)
  79. parsed_ok := any_addr != nil
  80. parsed: net.IP4_Address
  81. /*
  82. Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake.
  83. */
  84. switch addr in any_addr {
  85. case net.IP4_Address:
  86. parsed = addr
  87. case net.IP6_Address:
  88. parsed_ok = false
  89. msg = fmt.tprintf("parse_address mistook %v as IPv6 address %04x", vector.input, addr)
  90. expect(t, false, msg)
  91. }
  92. if !parsed_ok && valid {
  93. msg = fmt.tprintf("parse_ip4_address failed to parse %v, expected %v", vector.input, binstr_to_address(vector.binstr))
  94. } else if parsed_ok && !valid {
  95. msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected failure", vector.input, parsed)
  96. }
  97. expect(t, parsed_ok == valid, msg)
  98. if valid && parsed_ok {
  99. actual_binary := address_to_binstr(parsed)
  100. msg = fmt.tprintf("parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
  101. expect(t, actual_binary == vector.binstr, msg)
  102. /*
  103. Do we turn an address back into the same string properly?
  104. No point in testing the roundtrip if the first part failed.
  105. */
  106. if len(vector.output) > 0 && actual_binary == vector.binstr {
  107. stringified := net.address_to_string(parsed)
  108. msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
  109. expect(t, stringified == vector.output, msg)
  110. }
  111. }
  112. case .IP6:
  113. /*
  114. Do we parse the address properly?
  115. */
  116. parsed, parsed_ok := net.parse_ip6_address(vector.input)
  117. if !parsed_ok && valid {
  118. msg = fmt.tprintf("parse_ip6_address failed to parse %v, expected %04x", vector.input, binstr_to_address(vector.binstr))
  119. } else if parsed_ok && !valid {
  120. msg = fmt.tprintf("parse_ip6_address parsed %v into %04x, expected failure", vector.input, parsed)
  121. }
  122. expect(t, parsed_ok == valid, msg)
  123. if valid && parsed_ok {
  124. actual_binary := address_to_binstr(parsed)
  125. msg = fmt.tprintf("parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
  126. expect(t, actual_binary == vector.binstr, msg)
  127. /*
  128. Do we turn an address back into the same string properly?
  129. No point in testing the roundtrip if the first part failed.
  130. */
  131. if len(vector.output) > 0 && actual_binary == vector.binstr {
  132. stringified := net.address_to_string(parsed)
  133. msg = fmt.tprintf("address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
  134. expect(t, stringified == vector.output, msg)
  135. }
  136. }
  137. }
  138. }
  139. }
  140. address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
  141. switch t in address {
  142. case net.IP4_Address:
  143. b := transmute(u32be)t
  144. return fmt.tprintf("%08x", b)
  145. case net.IP6_Address:
  146. b := transmute(u128be)t
  147. return fmt.tprintf("%32x", b)
  148. case:
  149. return ""
  150. }
  151. unreachable()
  152. }
  153. binstr_to_address :: proc(binstr: string) -> (address: net.Address) {
  154. switch len(binstr) {
  155. case 8: // IPv4
  156. a, ok := strconv.parse_u64_of_base(binstr, 16)
  157. expect(t, ok, "failed to parse test case bin string")
  158. ipv4 := u32be(a)
  159. return net.IP4_Address(transmute([4]u8)ipv4)
  160. case 32: // IPv6
  161. a, ok := strconv.parse_u128_of_base(binstr, 16)
  162. expect(t, ok, "failed to parse test case bin string")
  163. ipv4 := u128be(a)
  164. return net.IP6_Address(transmute([8]u16be)ipv4)
  165. case 0:
  166. return nil
  167. }
  168. panic("Invalid test case")
  169. }
  170. Kind :: enum {
  171. IP4, // Decimal IPv4
  172. IP4_Alt, // Non-decimal address
  173. IP6, // Hex IPv6 or mixed IPv4/IPv6.
  174. }
  175. IP_Address_Parsing_Test_Vector :: struct {
  176. // Give it to the IPv4 or IPv6 parser?
  177. family: Kind,
  178. // Input address to try and parse.
  179. input: string,
  180. /*
  181. Hexadecimal representation of the expected numeric value of the address.
  182. Zero length means input is invalid and the parser should report failure.
  183. */
  184. binstr: string,
  185. // Expected `address_to_string` output, if a valid input and this string is non-empty.
  186. output: string,
  187. }
  188. IP_Address_Parsing_Test_Vectors :: []IP_Address_Parsing_Test_Vector{
  189. // dotted-decimal notation
  190. { .IP4, "0.0.0.0", "00000000", "0.0.0.0" },
  191. { .IP4, "127.0.0.1", "7f000001", "127.0.0.1" },
  192. { .IP4, "10.0.128.31", "0a00801f", "10.0.128.31" },
  193. { .IP4, "255.255.255.255", "ffffffff", "255.255.255.255"},
  194. // Odin custom: Address + port, valid
  195. { .IP4, "0.0.0.0:80", "00000000", "0.0.0.0" },
  196. { .IP4, "127.0.0.1:80", "7f000001", "127.0.0.1" },
  197. { .IP4, "10.0.128.31:80", "0a00801f", "10.0.128.31" },
  198. { .IP4, "255.255.255.255:80", "ffffffff", "255.255.255.255"},
  199. { .IP4, "[0.0.0.0]:80", "00000000", "0.0.0.0" },
  200. { .IP4, "[127.0.0.1]:80", "7f000001", "127.0.0.1" },
  201. { .IP4, "[10.0.128.31]:80", "0a00801f", "10.0.128.31" },
  202. { .IP4, "[255.255.255.255]:80", "ffffffff", "255.255.255.255"},
  203. // Odin custom: Address + port, invalid
  204. { .IP4, "[]:80", "", ""},
  205. { .IP4, "[0.0.0.0]", "", ""},
  206. { .IP4, "[127.0.0.1]:", "", ""},
  207. { .IP4, "[10.0.128.31] :80", "", ""},
  208. { .IP4, "[255.255.255.255]:65536", "", ""},
  209. // numbers-and-dots notation, but not dotted-decimal
  210. { .IP4_Alt, "1.2.03.4", "01020304", ""},
  211. { .IP4_Alt, "1.2.0x33.4", "01023304", ""},
  212. { .IP4_Alt, "1.2.0XAB.4", "0102ab04", ""},
  213. { .IP4_Alt, "1.2.0xabcd", "0102abcd", ""},
  214. { .IP4_Alt, "1.0xabcdef", "01abcdef", ""},
  215. { .IP4_Alt, "0x01abcdef", "01abcdef", ""},
  216. { .IP4_Alt, "00377.0x0ff.65534", "fffffffe", ""},
  217. // invalid as decimal address
  218. { .IP4, "", "", ""},
  219. { .IP4, ".1.2.3", "", ""},
  220. { .IP4, "1..2.3", "", ""},
  221. { .IP4, "1.2.3.", "", ""},
  222. { .IP4, "1.2.3.4.5", "", ""},
  223. { .IP4, "1.2.3.a", "", ""},
  224. { .IP4, "1.256.2.3", "", ""},
  225. { .IP4, "1.2.4294967296.3", "", ""},
  226. { .IP4, "1.2.-4294967295.3", "", ""},
  227. { .IP4, "1.2. 3.4", "", ""},
  228. // invalid as non-decimal address
  229. { .IP4_Alt, "", "", ""},
  230. { .IP4_Alt, ".1.2.3", "", ""},
  231. { .IP4_Alt, "1..2.3", "", ""},
  232. { .IP4_Alt, "1.2.3.", "", ""},
  233. { .IP4_Alt, "1.2.3.4.5", "", ""},
  234. { .IP4_Alt, "1.2.3.a", "", ""},
  235. { .IP4_Alt, "1.256.2.3", "", ""},
  236. { .IP4_Alt, "1.2.4294967296.3", "", ""},
  237. { .IP4_Alt, "1.2.-4294967295.3", "", ""},
  238. { .IP4_Alt, "1.2. 3.4", "", ""},
  239. // Valid IPv6 addresses
  240. { .IP6, "::", "00000000000000000000000000000000", "::"},
  241. { .IP6, "::1", "00000000000000000000000000000001", "::1"},
  242. { .IP6, "::192.168.1.1", "000000000000000000000000c0a80101", "::c0a8:101"},
  243. { .IP6, "0000:0000:0000:0000:0000:ffff:255.255.255.255", "00000000000000000000ffffffffffff", "::ffff:ffff:ffff"},
  244. { .IP6, "0:0:0:0:0:0:192.168.1.1", "000000000000000000000000c0a80101", "::c0a8:101"},
  245. { .IP6, "0:0::0:0:0:192.168.1.1", "000000000000000000000000c0a80101", "::c0a8:101"},
  246. { .IP6, "::ffff:192.168.1.1", "00000000000000000000ffffc0a80101", "::ffff:c0a8:101"},
  247. { .IP6, "a:0b:00c:000d:E:F::", "000a000b000c000d000e000f00000000", "a:b:c:d:e:f::"},
  248. { .IP6, "1:2:3:4:5:6::", "00010002000300040005000600000000", "1:2:3:4:5:6::"},
  249. { .IP6, "1:2:3:4:5:6:7::", "00010002000300040005000600070000", "1:2:3:4:5:6:7:0"},
  250. { .IP6, "::1:2:3:4:5:6", "00000000000100020003000400050006", "::1:2:3:4:5:6"},
  251. { .IP6, "::1:2:3:4:5:6:7", "00000001000200030004000500060007", "0:1:2:3:4:5:6:7"},
  252. { .IP6, "a:b::c:d:e:f", "000a000b00000000000c000d000e000f", "a:b::c:d:e:f"},
  253. { .IP6, "0:0:0:0:0:ffff:c0a8:5e4", "00000000000000000000ffffc0a805e4", "::ffff:c0a8:5e4"},
  254. { .IP6, "0::ffff:c0a8:5e4", "00000000000000000000ffffc0a805e4", "::ffff:c0a8:5e4"},
  255. // If multiple zero runs are present, shorten the longest one.
  256. { .IP6, "1:0:0:2:0:0:0:3", "00010000000000020000000000000003", "1:0:0:2::3"},
  257. // Invalid IPv6 addresses
  258. { .IP6, "", "", ""},
  259. { .IP6, ":", "", ""},
  260. { .IP6, ":::", "", ""},
  261. { .IP6, "192.168.1.1", "", ""},
  262. { .IP6, ":192.168.1.1", "", ""},
  263. { .IP6, "::012.34.56.78", "", ""},
  264. { .IP6, ":ffff:192.168.1.1", "", ""},
  265. { .IP6, ".192.168.1.1", "", ""},
  266. { .IP6, ":.192.168.1.1", "", ""},
  267. { .IP6, "a:0b:00c:000d:0000e:f::", "", ""},
  268. { .IP6, "1:2:3:4:5:6:7:8::", "", ""},
  269. { .IP6, "1:2:3:4:5:6:7::9", "", ""},
  270. { .IP6, "::1:2:3:4:5:6:7:8", "", ""},
  271. { .IP6, "ffff:c0a8:5e4", "", ""},
  272. { .IP6, ":ffff:c0a8:5e4", "", ""},
  273. { .IP6, "0:0:0:0:ffff:c0a8:5e4", "", ""},
  274. { .IP6, "::0::ffff:c0a8:5e4", "", ""},
  275. { .IP6, "c0a8", "", ""},
  276. }
  277. ENDPOINT := net.Endpoint{
  278. net.IP4_Address{127, 0, 0, 1},
  279. 9999,
  280. }
  281. CONTENT := "Hellope!"
  282. SEND_TIMEOUT :: time.Duration(1 * time.Second)
  283. RECV_TIMEOUT :: time.Duration(1 * time.Second)
  284. Thread_Data :: struct {
  285. skt: net.Any_Socket,
  286. err: net.Network_Error,
  287. tid: ^thread.Thread,
  288. no_accept: bool, // Tell the server proc not to accept.
  289. data: [1024]u8, // Received data and its length
  290. length: int,
  291. }
  292. thread_data := [3]Thread_Data{}
  293. /*
  294. This runs a bunch of socket tests using threads:
  295. - two servers trying to bind the same endpoint
  296. - client trying to connect to closed port
  297. - client trying to connect to an open port with a non-accepting server
  298. - client sending server data and server sending client data
  299. - etc.
  300. */
  301. tcp_tests :: proc(t: ^testing.T) {
  302. fmt.println("Testing two servers trying to bind to the same endpoint...")
  303. two_servers_binding_same_endpoint(t)
  304. fmt.println("Testing client connecting to a closed port...")
  305. client_connects_to_closed_port(t)
  306. fmt.println("Testing client connecting to port that doesn't accept...")
  307. client_connects_to_open_but_non_accepting_port(t)
  308. fmt.println("Testing client sending server data...")
  309. client_sends_server_data(t)
  310. }
  311. tcp_client :: proc(retval: rawptr) {
  312. send :: proc(content: []u8) -> (err: net.Network_Error) {
  313. skt := net.dial_tcp(ENDPOINT) or_return
  314. defer net.close(skt)
  315. net.set_option(skt, .Send_Timeout, SEND_TIMEOUT)
  316. net.set_option(skt, .Receive_Timeout, RECV_TIMEOUT)
  317. _, err = net.send(skt, content)
  318. return
  319. }
  320. r := transmute(^Thread_Data)retval
  321. r.err = send(transmute([]u8)CONTENT)
  322. return
  323. }
  324. tcp_server :: proc(retval: rawptr) {
  325. r := transmute(^Thread_Data)retval
  326. if r.skt, r.err = net.listen_tcp(ENDPOINT); r.err != nil {
  327. return
  328. }
  329. defer net.close(r.skt)
  330. if r.no_accept {
  331. // Don't accept any connections, just listen.
  332. return
  333. }
  334. client: net.TCP_Socket
  335. if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil {
  336. return
  337. }
  338. defer net.close(client)
  339. r.length, r.err = net.recv_tcp(client, r.data[:])
  340. return
  341. }
  342. cleanup_thread :: proc(data: Thread_Data) {
  343. net.close(data.skt)
  344. thread.terminate(data.tid, 1)
  345. thread.destroy(data.tid)
  346. }
  347. two_servers_binding_same_endpoint :: proc(t: ^testing.T) {
  348. thread_data = {}
  349. thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
  350. thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_server, context)
  351. defer {
  352. cleanup_thread(thread_data[0])
  353. cleanup_thread(thread_data[1])
  354. }
  355. // Give the two servers enough time to try and bind the same endpoint
  356. time.sleep(1 * time.Second)
  357. first_won := thread_data[0].err == nil && thread_data[1].err == net.Bind_Error.Address_In_Use
  358. second_won := thread_data[1].err == nil && thread_data[0].err == net.Bind_Error.Address_In_Use
  359. okay := first_won || second_won
  360. msg := fmt.tprintf("Expected servers to return `nil` and `Address_In_Use`, got %v and %v", thread_data[0].err, thread_data[1].err)
  361. expect(t, okay, msg)
  362. }
  363. client_connects_to_closed_port :: proc(t: ^testing.T) {
  364. thread_data = {}
  365. thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_client, context)
  366. defer {
  367. cleanup_thread(thread_data[0])
  368. }
  369. // Give the socket enough time to return `Refused`
  370. time.sleep(4 * time.Second)
  371. okay := thread_data[0].err == net.Dial_Error.Refused
  372. msg := fmt.tprintf("Expected client to return `Refused` connecting to closed port, got %v", thread_data[0].err)
  373. expect(t, okay, msg)
  374. }
  375. client_connects_to_open_but_non_accepting_port :: proc(t: ^testing.T) {
  376. thread_data = {}
  377. // Tell server proc not to accept
  378. thread_data[0].no_accept = true
  379. thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
  380. thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context)
  381. defer {
  382. cleanup_thread(thread_data[0])
  383. cleanup_thread(thread_data[1])
  384. }
  385. // Give the two servers enough time to try and bind the same endpoint
  386. time.sleep(4 * time.Second)
  387. okay := thread_data[0].err == nil && thread_data[1].err == net.Dial_Error.Refused
  388. msg := fmt.tprintf("Expected server and client to return `nil` and `Refused`, got %v and %v", thread_data[0].err, thread_data[1].err)
  389. expect(t, okay, msg)
  390. }
  391. client_sends_server_data :: proc(t: ^testing.T) {
  392. thread_data = {}
  393. // Tell server proc not to accept
  394. // thread_data[0].no_accept = true
  395. thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
  396. thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context)
  397. defer {
  398. cleanup_thread(thread_data[0])
  399. cleanup_thread(thread_data[1])
  400. }
  401. // Give the two servers enough time to try and bind the same endpoint
  402. time.sleep(1 * time.Second)
  403. okay := thread_data[0].err == nil && thread_data[1].err == nil
  404. msg := fmt.tprintf("Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err)
  405. expect(t, okay, msg)
  406. received := string(thread_data[0].data[:thread_data[0].length])
  407. okay = received == CONTENT
  408. msg = fmt.tprintf("Expected client to send \"{}\", got \"{}\"", CONTENT, received)
  409. expect(t, okay, msg)
  410. }