test_core_net.odin 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /*
  2. Copyright 2021 Jeroen van Rijn <[email protected]>.
  3. Copyright 2024 Feoramund <[email protected]>.
  4. Made available under Odin's BSD-3 license.
  5. List of contributors:
  6. Jeroen van Rijn: Initial implementation.
  7. graphitemaster: pton/ntop IANA test vectors
  8. Feoramund: FreeBSD-specific tests.
  9. A test suite for `core:net`
  10. */
  11. //+build !netbsd
  12. //+build !openbsd
  13. package test_core_net
  14. import "core:testing"
  15. import "core:net"
  16. import "core:strconv"
  17. import "core:sync"
  18. import "core:time"
  19. import "core:thread"
  20. import "core:fmt"
  21. import "core:log"
  22. @test
  23. address_parsing_test :: proc(t: ^testing.T) {
  24. for vector in IP_Address_Parsing_Test_Vectors {
  25. kind := ""
  26. switch vector.family {
  27. case .IP4: kind = "[IPv4]"
  28. case .IP4_Alt: kind = "[IPv4 Non-Decimal]"
  29. case .IP6: kind = "[IPv6]"
  30. case: panic("Add support to the test for this type.")
  31. }
  32. valid := len(vector.binstr) > 0
  33. switch vector.family {
  34. case .IP4, .IP4_Alt:
  35. // Does `net.parse_ip4_address` think we parsed the address properly?
  36. non_decimal := vector.family == .IP4_Alt
  37. any_addr := net.parse_address(vector.input, non_decimal)
  38. parsed_ok := any_addr != nil
  39. parsed: net.IP4_Address
  40. // Ensure that `parse_address` doesn't parse IPv4 addresses into IPv6 addreses by mistake.
  41. switch addr in any_addr {
  42. case net.IP4_Address:
  43. parsed = addr
  44. case net.IP6_Address:
  45. parsed_ok = false
  46. testing.expectf(t, false, "parse_address mistook %v as IPv6 address %04x", vector.input, addr)
  47. }
  48. if !parsed_ok && valid {
  49. testing.expectf(t, parsed_ok == valid, "parse_ip4_address failed to parse %v, expected %v", vector.input, binstr_to_address(t, vector.binstr))
  50. } else if parsed_ok && !valid {
  51. testing.expectf(t, parsed_ok == valid, "parse_ip4_address parsed %v into %v, expected failure", vector.input, parsed)
  52. }
  53. if valid && parsed_ok {
  54. actual_binary := address_to_binstr(parsed)
  55. testing.expectf(t, actual_binary == vector.binstr, "parse_ip4_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
  56. // Do we turn an address back into the same string properly? No point in testing the roundtrip if the first part failed.
  57. if len(vector.output) > 0 && actual_binary == vector.binstr {
  58. stringified := net.address_to_string(parsed)
  59. testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
  60. }
  61. }
  62. case .IP6:
  63. // Do we parse the address properly?
  64. parsed, parsed_ok := net.parse_ip6_address(vector.input)
  65. if !parsed_ok && valid {
  66. testing.expectf(t, parsed_ok == valid, "parse_ip6_address failed to parse %v, expected %04x", vector.input, binstr_to_address(t, vector.binstr))
  67. } else if parsed_ok && !valid {
  68. testing.expectf(t, parsed_ok == valid, "parse_ip6_address parsed %v into %04x, expected failure", vector.input, parsed)
  69. }
  70. if valid && parsed_ok {
  71. actual_binary := address_to_binstr(parsed)
  72. testing.expectf(t, actual_binary == vector.binstr, "parse_ip6_address parsed %v into %v, expected %v", vector.input, actual_binary, vector.binstr)
  73. // Do we turn an address back into the same string properly? No point in testing the roundtrip if the first part failed.
  74. if len(vector.output) > 0 && actual_binary == vector.binstr {
  75. stringified := net.address_to_string(parsed)
  76. testing.expectf(t, stringified == vector.output, "address_to_string turned %v into %v, expected %v", parsed, stringified, vector.output)
  77. }
  78. }
  79. }
  80. }
  81. }
  82. Kind :: enum {
  83. IP4, // Decimal IPv4
  84. IP4_Alt, // Non-decimal address
  85. IP6, // Hex IPv6 or mixed IPv4/IPv6.
  86. }
  87. IP_Address_Parsing_Test_Vector :: struct {
  88. // Give it to the IPv4 or IPv6 parser?
  89. family: Kind,
  90. // Input address to try and parse.
  91. input: string,
  92. // Hexadecimal representation of the expected numeric value of the address. Zero length means input is invalid and the parser should report failure.
  93. binstr: string,
  94. // Expected `address_to_string` output, if a valid input and this string is non-empty.
  95. output: string,
  96. }
  97. IP_Address_Parsing_Test_Vectors :: []IP_Address_Parsing_Test_Vector{
  98. // dotted-decimal notation
  99. { .IP4, "0.0.0.0", "00000000", "0.0.0.0" },
  100. { .IP4, "127.0.0.1", "7f000001", "127.0.0.1" },
  101. { .IP4, "10.0.128.31", "0a00801f", "10.0.128.31" },
  102. { .IP4, "255.255.255.255", "ffffffff", "255.255.255.255"},
  103. // Odin custom: Address + port, valid
  104. { .IP4, "0.0.0.0:80", "00000000", "0.0.0.0" },
  105. { .IP4, "127.0.0.1:80", "7f000001", "127.0.0.1" },
  106. { .IP4, "10.0.128.31:80", "0a00801f", "10.0.128.31" },
  107. { .IP4, "255.255.255.255:80", "ffffffff", "255.255.255.255"},
  108. { .IP4, "[0.0.0.0]:80", "00000000", "0.0.0.0" },
  109. { .IP4, "[127.0.0.1]:80", "7f000001", "127.0.0.1" },
  110. { .IP4, "[10.0.128.31]:80", "0a00801f", "10.0.128.31" },
  111. { .IP4, "[255.255.255.255]:80", "ffffffff", "255.255.255.255"},
  112. // Odin custom: Address + port, invalid
  113. { .IP4, "[]:80", "", ""},
  114. { .IP4, "[0.0.0.0]", "", ""},
  115. { .IP4, "[127.0.0.1]:", "", ""},
  116. { .IP4, "[10.0.128.31] :80", "", ""},
  117. { .IP4, "[255.255.255.255]:65536", "", ""},
  118. // numbers-and-dots notation, but not dotted-decimal
  119. { .IP4_Alt, "1.2.03.4", "01020304", ""},
  120. { .IP4_Alt, "1.2.0x33.4", "01023304", ""},
  121. { .IP4_Alt, "1.2.0XAB.4", "0102ab04", ""},
  122. { .IP4_Alt, "1.2.0xabcd", "0102abcd", ""},
  123. { .IP4_Alt, "1.0xabcdef", "01abcdef", ""},
  124. { .IP4_Alt, "0x01abcdef", "01abcdef", ""},
  125. { .IP4_Alt, "00377.0x0ff.65534", "fffffffe", ""},
  126. // invalid as decimal address
  127. { .IP4, "", "", ""},
  128. { .IP4, ".1.2.3", "", ""},
  129. { .IP4, "1..2.3", "", ""},
  130. { .IP4, "1.2.3.", "", ""},
  131. { .IP4, "1.2.3.4.5", "", ""},
  132. { .IP4, "1.2.3.a", "", ""},
  133. { .IP4, "1.256.2.3", "", ""},
  134. { .IP4, "1.2.4294967296.3", "", ""},
  135. { .IP4, "1.2.-4294967295.3", "", ""},
  136. { .IP4, "1.2. 3.4", "", ""},
  137. // invalid as non-decimal address
  138. { .IP4_Alt, "", "", ""},
  139. { .IP4_Alt, ".1.2.3", "", ""},
  140. { .IP4_Alt, "1..2.3", "", ""},
  141. { .IP4_Alt, "1.2.3.", "", ""},
  142. { .IP4_Alt, "1.2.3.4.5", "", ""},
  143. { .IP4_Alt, "1.2.3.a", "", ""},
  144. { .IP4_Alt, "1.256.2.3", "", ""},
  145. { .IP4_Alt, "1.2.4294967296.3", "", ""},
  146. { .IP4_Alt, "1.2.-4294967295.3", "", ""},
  147. { .IP4_Alt, "1.2. 3.4", "", ""},
  148. // Valid IPv6 addresses
  149. { .IP6, "::", "00000000000000000000000000000000", "::"},
  150. { .IP6, "::1", "00000000000000000000000000000001", "::1"},
  151. { .IP6, "::192.168.1.1", "000000000000000000000000c0a80101", "::c0a8:101"},
  152. { .IP6, "0000:0000:0000:0000:0000:ffff:255.255.255.255", "00000000000000000000ffffffffffff", "::ffff:ffff:ffff"},
  153. { .IP6, "0:0:0:0:0:0:192.168.1.1", "000000000000000000000000c0a80101", "::c0a8:101"},
  154. { .IP6, "0:0::0:0:0:192.168.1.1", "000000000000000000000000c0a80101", "::c0a8:101"},
  155. { .IP6, "::ffff:192.168.1.1", "00000000000000000000ffffc0a80101", "::ffff:c0a8:101"},
  156. { .IP6, "a:0b:00c:000d:E:F::", "000a000b000c000d000e000f00000000", "a:b:c:d:e:f::"},
  157. { .IP6, "1:2:3:4:5:6::", "00010002000300040005000600000000", "1:2:3:4:5:6::"},
  158. { .IP6, "1:2:3:4:5:6:7::", "00010002000300040005000600070000", "1:2:3:4:5:6:7:0"},
  159. { .IP6, "::1:2:3:4:5:6", "00000000000100020003000400050006", "::1:2:3:4:5:6"},
  160. { .IP6, "::1:2:3:4:5:6:7", "00000001000200030004000500060007", "0:1:2:3:4:5:6:7"},
  161. { .IP6, "a:b::c:d:e:f", "000a000b00000000000c000d000e000f", "a:b::c:d:e:f"},
  162. { .IP6, "0:0:0:0:0:ffff:c0a8:5e4", "00000000000000000000ffffc0a805e4", "::ffff:c0a8:5e4"},
  163. { .IP6, "0::ffff:c0a8:5e4", "00000000000000000000ffffc0a805e4", "::ffff:c0a8:5e4"},
  164. // If multiple zero runs are present, shorten the longest one.
  165. { .IP6, "1:0:0:2:0:0:0:3", "00010000000000020000000000000003", "1:0:0:2::3"},
  166. // Invalid IPv6 addresses
  167. { .IP6, "", "", ""},
  168. { .IP6, ":", "", ""},
  169. { .IP6, ":::", "", ""},
  170. { .IP6, "192.168.1.1", "", ""},
  171. { .IP6, ":192.168.1.1", "", ""},
  172. { .IP6, "::012.34.56.78", "", ""},
  173. { .IP6, ":ffff:192.168.1.1", "", ""},
  174. { .IP6, ".192.168.1.1", "", ""},
  175. { .IP6, ":.192.168.1.1", "", ""},
  176. { .IP6, "a:0b:00c:000d:0000e:f::", "", ""},
  177. { .IP6, "1:2:3:4:5:6:7:8::", "", ""},
  178. { .IP6, "1:2:3:4:5:6:7::9", "", ""},
  179. { .IP6, "::1:2:3:4:5:6:7:8", "", ""},
  180. { .IP6, "ffff:c0a8:5e4", "", ""},
  181. { .IP6, ":ffff:c0a8:5e4", "", ""},
  182. { .IP6, "0:0:0:0:ffff:c0a8:5e4", "", ""},
  183. { .IP6, "::0::ffff:c0a8:5e4", "", ""},
  184. { .IP6, "c0a8", "", ""},
  185. }
  186. ENDPOINT_TWO_SERVERS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9991}
  187. ENDPOINT_CLOSED_PORT := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9992}
  188. ENDPOINT_SERVER_SENDS := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9993}
  189. ENDPOINT_UDP_ECHO := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9994}
  190. ENDPOINT_NONBLOCK := net.Endpoint{net.IP4_Address{127, 0, 0, 1}, 9995}
  191. @(test)
  192. two_servers_binding_same_endpoint :: proc(t: ^testing.T) {
  193. skt1, err1 := net.listen_tcp(ENDPOINT_TWO_SERVERS)
  194. defer net.close(skt1)
  195. skt2, err2 := net.listen_tcp(ENDPOINT_TWO_SERVERS)
  196. defer net.close(skt2)
  197. testing.expect(t, err1 == nil, "expected first server binding to endpoint to do so without error")
  198. testing.expect(t, err2 == net.Bind_Error.Address_In_Use, "expected second server to bind to an endpoint to return .Address_In_Use")
  199. }
  200. @(test)
  201. client_connects_to_closed_port :: proc(t: ^testing.T) {
  202. skt, err := net.dial_tcp(ENDPOINT_CLOSED_PORT)
  203. defer net.close(skt)
  204. testing.expect(t, err == net.Dial_Error.Refused, "expected dial of a closed endpoint to return .Refused")
  205. }
  206. @(test)
  207. client_sends_server_data :: proc(t: ^testing.T) {
  208. CONTENT: string: "Hellope!"
  209. SEND_TIMEOUT :: time.Duration(1 * time.Second)
  210. RECV_TIMEOUT :: time.Duration(1 * time.Second)
  211. Thread_Data :: struct {
  212. t: ^testing.T,
  213. skt: net.Any_Socket,
  214. err: net.Network_Error,
  215. tid: ^thread.Thread,
  216. data: [1024]u8, // Received data and its length
  217. length: int,
  218. wg: ^sync.Wait_Group,
  219. }
  220. tcp_client :: proc(thread_data: rawptr) {
  221. r := cast(^Thread_Data)thread_data
  222. defer sync.wait_group_done(r.wg)
  223. if r.skt, r.err = net.dial_tcp(ENDPOINT_SERVER_SENDS); r.err != nil {
  224. testing.expectf(r.t, false, "[tcp_client:dial_tcp] %v", r.err)
  225. return
  226. }
  227. net.set_option(r.skt, .Send_Timeout, SEND_TIMEOUT)
  228. _, r.err = net.send(r.skt, transmute([]byte)CONTENT)
  229. }
  230. tcp_server :: proc(thread_data: rawptr) {
  231. r := cast(^Thread_Data)thread_data
  232. defer sync.wait_group_done(r.wg)
  233. if r.skt, r.err = net.listen_tcp(ENDPOINT_SERVER_SENDS); r.err != nil {
  234. sync.wait_group_done(r.wg)
  235. testing.expectf(r.t, false, "[tcp_server:listen_tcp] %v", r.err)
  236. return
  237. }
  238. sync.wait_group_done(r.wg)
  239. client: net.TCP_Socket
  240. if client, _, r.err = net.accept_tcp(r.skt.(net.TCP_Socket)); r.err != nil {
  241. testing.expectf(r.t, false, "[tcp_server:accept_tcp] %v", r.err)
  242. return
  243. }
  244. defer net.close(client)
  245. net.set_option(client, .Receive_Timeout, RECV_TIMEOUT)
  246. r.length, r.err = net.recv_tcp(client, r.data[:])
  247. return
  248. }
  249. thread_data := [2]Thread_Data{}
  250. wg: sync.Wait_Group
  251. sync.wait_group_add(&wg, 1)
  252. thread_data[0].t = t
  253. thread_data[0].wg = &wg
  254. thread_data[0].tid = thread.create_and_start_with_data(&thread_data[0], tcp_server, context)
  255. sync.wait_group_wait(&wg)
  256. sync.wait_group_add(&wg, 2)
  257. thread_data[1].t = t
  258. thread_data[1].wg = &wg
  259. thread_data[1].tid = thread.create_and_start_with_data(&thread_data[1], tcp_client, context)
  260. defer {
  261. net.close(thread_data[0].skt)
  262. thread.destroy(thread_data[0].tid)
  263. net.close(thread_data[1].skt)
  264. thread.destroy(thread_data[1].tid)
  265. }
  266. sync.wait_group_wait(&wg)
  267. okay := thread_data[0].err == nil && thread_data[1].err == nil
  268. testing.expectf(t, okay, "Expected client and server to return `nil`, got %v and %v", thread_data[0].err, thread_data[1].err)
  269. received := string(thread_data[0].data[:thread_data[0].length])
  270. okay = received == CONTENT
  271. testing.expectf(t, okay, "Expected client to send \"{}\", got \"{}\"", CONTENT, received)
  272. }
  273. URL_Test :: struct {
  274. scheme, host, path: string,
  275. queries: map[string]string,
  276. fragment: string,
  277. url: []string,
  278. }
  279. @test
  280. split_url_test :: proc(t: ^testing.T) {
  281. test_cases := []URL_Test{
  282. {
  283. "http", "example.com", "/",
  284. {}, "",
  285. {"http://example.com"},
  286. },
  287. {
  288. "https", "odin-lang.org", "/",
  289. {}, "",
  290. {"https://odin-lang.org"},
  291. },
  292. {
  293. "https", "odin-lang.org", "/docs/",
  294. {}, "",
  295. {"https://odin-lang.org/docs/"},
  296. },
  297. {
  298. "https", "odin-lang.org", "/docs/overview",
  299. {}, "",
  300. {"https://odin-lang.org/docs/overview"},
  301. },
  302. {
  303. "http", "example.com", "/",
  304. {"a" = "b"}, "",
  305. {"http://example.com?a=b"},
  306. },
  307. {
  308. "http", "example.com", "/",
  309. {"a" = ""}, "",
  310. {"http://example.com?a"},
  311. },
  312. {
  313. "http", "example.com", "/",
  314. {"a" = "b", "c" = "d"}, "",
  315. {"http://example.com?a=b&c=d"},
  316. },
  317. {
  318. "http", "example.com", "/",
  319. {"a" = "", "c" = "d"}, "",
  320. {"http://example.com?a&c=d"},
  321. },
  322. {
  323. "http", "example.com", "/example",
  324. {"a" = "", "b" = ""}, "",
  325. {"http://example.com/example?a&b"},
  326. },
  327. {
  328. "https", "example.com", "/callback",
  329. {"redirect" = "https://other.com/login"}, "",
  330. {"https://example.com/callback?redirect=https://other.com/login"},
  331. },
  332. {
  333. "http", "example.com", "/",
  334. {}, "Hellope",
  335. {"http://example.com#Hellope"},
  336. },
  337. {
  338. "https", "odin-lang.org", "/",
  339. {"a" = ""}, "Hellope",
  340. {"https://odin-lang.org?a#Hellope"},
  341. },
  342. {
  343. "http", "example.com", "/",
  344. {"a" = "b"}, "BeesKnees",
  345. {"http://example.com?a=b#BeesKnees"},
  346. },
  347. {
  348. "https", "odin-lang.org", "/docs/overview/",
  349. {}, "hellope",
  350. {"https://odin-lang.org/docs/overview/#hellope"},
  351. },
  352. }
  353. for test in test_cases {
  354. scheme, host, path, queries, fragment := net.split_url(test.url[0])
  355. defer {
  356. delete(queries)
  357. delete(test.queries)
  358. }
  359. testing.expectf(t, scheme == test.scheme, "Expected `net.split_url` to return %s, got %s", test.scheme, scheme)
  360. testing.expectf(t, host == test.host, "Expected `net.split_url` to return %s, got %s", test.host, host)
  361. testing.expectf(t, path == test.path, "Expected `net.split_url` to return %s, got %s", test.path, path)
  362. testing.expectf(t, len(queries) == len(test.queries), "Expected `net.split_url` to return %d queries, got %d queries", len(test.queries), len(queries))
  363. for k, v in queries {
  364. expected := test.queries[k]
  365. testing.expectf(t, v == expected, "Expected `net.split_url` to return %s, got %s", expected, v)
  366. }
  367. testing.expectf(t, fragment == test.fragment, "Expected `net.split_url` to return %s, got %s", test.fragment, fragment)
  368. }
  369. }
  370. @test
  371. join_url_test :: proc(t: ^testing.T) {
  372. test_cases := []URL_Test{
  373. {
  374. "http", "example.com", "/",
  375. {}, "",
  376. {"http://example.com/"},
  377. },
  378. {
  379. "https", "odin-lang.org", "/",
  380. {}, "",
  381. {"https://odin-lang.org/"},
  382. },
  383. {
  384. "https", "odin-lang.org", "/docs/",
  385. {}, "",
  386. {"https://odin-lang.org/docs/"},
  387. },
  388. {
  389. "https", "odin-lang.org", "/docs/overview",
  390. {}, "",
  391. {"https://odin-lang.org/docs/overview"},
  392. },
  393. {
  394. "http", "example.com", "/",
  395. {"a" = "b"}, "",
  396. {"http://example.com/?a=b"},
  397. },
  398. {
  399. "http", "example.com", "/",
  400. {"a" = ""}, "",
  401. {"http://example.com/?a"},
  402. },
  403. {
  404. "http", "example.com", "/",
  405. {"a" = "b", "c" = "d"}, "",
  406. {"http://example.com/?a=b&c=d", "http://example.com/?c=d&a=b"},
  407. },
  408. {
  409. "http", "example.com", "/",
  410. {"a" = "", "c" = "d"}, "",
  411. {"http://example.com/?a&c=d", "http://example.com/?c=d&a"},
  412. },
  413. {
  414. "http", "example.com", "/example",
  415. {"a" = "", "b" = ""}, "",
  416. {"http://example.com/example?a&b", "http://example.com/example?b&a"},
  417. },
  418. {
  419. "http", "example.com", "/",
  420. {}, "Hellope",
  421. {"http://example.com/#Hellope"},
  422. },
  423. {
  424. "https", "odin-lang.org", "/",
  425. {"a" = ""}, "Hellope",
  426. {"https://odin-lang.org/?a#Hellope"},
  427. },
  428. {
  429. "http", "example.com", "/",
  430. {"a" = "b"}, "BeesKnees",
  431. {"http://example.com/?a=b#BeesKnees"},
  432. },
  433. {
  434. "https", "odin-lang.org", "/docs/overview/",
  435. {}, "hellope",
  436. {"https://odin-lang.org/docs/overview/#hellope"},
  437. },
  438. }
  439. for test in test_cases {
  440. url := net.join_url(test.scheme, test.host, test.path, test.queries, test.fragment)
  441. defer {
  442. delete(url)
  443. delete(test.queries)
  444. }
  445. pass := false
  446. for test_url in test.url {
  447. pass |= url == test_url
  448. }
  449. testing.expectf(t, pass, "Expected `net.join_url` to return one of %s, got %s", test.url, url)
  450. }
  451. }
  452. @test
  453. test_udp_echo :: proc(t: ^testing.T) {
  454. server, make_server_err := net.make_unbound_udp_socket(.IP4)
  455. if !testing.expect_value(t, make_server_err, nil) {
  456. return
  457. }
  458. defer net.close(server)
  459. bind_server_err := net.bind(server, ENDPOINT_UDP_ECHO)
  460. if !testing.expect_value(t, bind_server_err, nil) {
  461. return
  462. }
  463. client, make_client_err := net.make_unbound_udp_socket(.IP4)
  464. if !testing.expect_value(t, make_client_err, nil) {
  465. return
  466. }
  467. defer net.close(client)
  468. msg := "Hellope world!"
  469. buf: [64]u8
  470. bytes_written, send_err := net.send_udp(client, transmute([]u8)msg[:], ENDPOINT_UDP_ECHO)
  471. if !testing.expect_value(t, send_err, nil) {
  472. return
  473. }
  474. if !testing.expect_value(t, bytes_written, len(msg)) {
  475. return
  476. }
  477. bytes_read, _, read_err := net.recv_udp(server, buf[:])
  478. if !testing.expect_value(t, read_err, nil) {
  479. return
  480. }
  481. if !testing.expect_value(t, bytes_read, len(msg)) {
  482. return
  483. }
  484. testing.expect_value(t, msg, transmute(string)buf[:bytes_read])
  485. }
  486. @test
  487. test_dns_resolve :: proc(t: ^testing.T) {
  488. // NOTE: This test depends on external factors, so if it fails, an IP
  489. // address may have changed or become unavailable.
  490. // The net API returns only one address per protocol version, and DNS
  491. // records can store many, so we'll have to check all possibilities.
  492. ep4, ep6, resolve_err := net.resolve("dns.quad9.net")
  493. if !testing.expect_value(t, resolve_err, nil) {
  494. return
  495. }
  496. ip4, ip4_ok := ep4.address.(net.IP4_Address)
  497. if !testing.expect(t, ip4_ok, "Unable to resolve IP4") {
  498. return
  499. }
  500. valid_ip4_a := net.IP4_Address{ 9, 9, 9, 9}
  501. valid_ip4_b := net.IP4_Address{149, 112, 112, 112}
  502. if ip4 != valid_ip4_a && ip4 != valid_ip4_b {
  503. log.errorf("DNS resolved to invalid IP4: %v, expected %v or %v", ip4, valid_ip4_a, valid_ip4_b)
  504. }
  505. ip6, ip6_ok := ep6.address.(net.IP6_Address)
  506. if !testing.expect(t, ip6_ok, "Unable to resolve IP6") {
  507. return
  508. }
  509. valid_ip6_a := net.IP6_Address{0x2620, 0xfe, 0, 0, 0, 0, 0, 9}
  510. valid_ip6_b := net.IP6_Address{0x2620, 0xfe, 0, 0, 0, 0, 0, 0xfe}
  511. if ip6 != valid_ip6_a && ip6 != valid_ip6_b {
  512. log.errorf("DNS resolved to invalid IP6: %v, expected %v or %v", ip6, valid_ip6_a, valid_ip6_b)
  513. }
  514. }
  515. @test
  516. test_nonblocking_option :: proc(t: ^testing.T) {
  517. server, listen_err := net.listen_tcp(ENDPOINT_NONBLOCK)
  518. if !testing.expect_value(t, listen_err, nil) {
  519. return
  520. }
  521. defer net.close(server)
  522. testing.set_fail_timeout(t, 2 * time.Second)
  523. // If the nonblocking option isn't set correctly in the operating system,
  524. // this should block until the timeout hits.
  525. net.set_blocking(server, false)
  526. _, _, accept_err := net.accept_tcp(server)
  527. if !testing.expect_value(t, accept_err, net.Accept_Error.Would_Block) {
  528. return
  529. }
  530. }
  531. @(private)
  532. address_to_binstr :: proc(address: net.Address) -> (binstr: string) {
  533. switch t in address {
  534. case net.IP4_Address:
  535. b := transmute(u32be)t
  536. return fmt.tprintf("%08x", b)
  537. case net.IP6_Address:
  538. b := transmute(u128be)t
  539. return fmt.tprintf("%32x", b)
  540. case:
  541. return ""
  542. }
  543. unreachable()
  544. }
  545. @(private)
  546. binstr_to_address :: proc(t: ^testing.T, binstr: string) -> (address: net.Address) {
  547. switch len(binstr) {
  548. case 8: // IPv4
  549. a, ok := strconv.parse_u64_of_base(binstr, 16)
  550. testing.expect(t, ok, "failed to parse test case bin string")
  551. ipv4 := u32be(a)
  552. return net.IP4_Address(transmute([4]u8)ipv4)
  553. case 32: // IPv6
  554. a, ok := strconv.parse_u128_of_base(binstr, 16)
  555. testing.expect(t, ok, "failed to parse test case bin string")
  556. ipv4 := u128be(a)
  557. return net.IP6_Address(transmute([8]u16be)ipv4)
  558. case 0:
  559. return nil
  560. }
  561. panic("Invalid test case")
  562. }