1 // 2 // socket-related test cases 3 // 4 // Authors: 5 // Steffen Kiess (s-kiess@web.de) 6 // 7 // Copyright (C) 2015 Steffen Kiess 8 // 9 10 using System; 11 using System.IO; 12 using System.Net; 13 using System.Net.Sockets; 14 using System.Runtime.InteropServices; 15 16 using Mono.Unix; 17 using Mono.Unix.Native; 18 19 using NUnit.Framework; 20 21 namespace MonoTests.Mono.Unix.Native 22 { 23 [TestFixture, Category ("NotDotNet"), Category ("NotOnWindows")] 24 public class SocketTest { 25 26 string TempFolder; 27 28 [SetUp] SetUp()29 public void SetUp () 30 { 31 TempFolder = Path.Combine (Path.GetTempPath (), this.GetType ().FullName); 32 33 if (Directory.Exists (TempFolder)) 34 Directory.Delete (TempFolder, true); 35 36 Directory.CreateDirectory (TempFolder); 37 } 38 39 [TearDown] TearDown()40 public void TearDown() 41 { 42 if (Directory.Exists (TempFolder)) 43 Directory.Delete (TempFolder, true); 44 } 45 46 // Set a timeout on all sockets to make sure that if a test fails it 47 // won't cause the program to hang SetTimeout(int socket)48 void SetTimeout (int socket) 49 { 50 var timeout = new Timeval { 51 tv_sec = 0, 52 tv_usec = 500000, 53 }; 54 if (Syscall.setsockopt (socket, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_RCVTIMEO, timeout) < 0 || 55 Syscall.setsockopt (socket, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_SNDTIMEO, timeout) < 0) 56 UnixMarshal.ThrowExceptionForLastError (); 57 } 58 WithSocketPair(Action<int, int> f)59 void WithSocketPair (Action<int, int> f) 60 { 61 int socket1, socket2; 62 if (Syscall.socketpair (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, out socket1, out socket2) < 0) 63 UnixMarshal.ThrowExceptionForLastError (); 64 try { 65 SetTimeout (socket1); 66 SetTimeout (socket2); 67 68 f (socket1, socket2); 69 } finally { 70 int r0 = Syscall.close (socket1); 71 int r1 = Syscall.close (socket2); 72 if (r0 < 0 || r1 < 0) 73 UnixMarshal.ThrowExceptionForLastError (); 74 } 75 } 76 WithSockets(UnixAddressFamily af, UnixSocketType type, UnixSocketProtocol protocol, Action<int, int> f)77 void WithSockets (UnixAddressFamily af, UnixSocketType type, UnixSocketProtocol protocol, Action<int, int> f) 78 { 79 int so1, so2; 80 if ((so1 = Syscall.socket (af, type, protocol)) < 0) 81 UnixMarshal.ThrowExceptionForLastError (); 82 try { 83 if ((so2 = Syscall.socket (af, type, protocol)) < 0) 84 UnixMarshal.ThrowExceptionForLastError (); 85 try { 86 SetTimeout (so1); 87 SetTimeout (so2); 88 89 f (so1, so2); 90 } finally { 91 if (Syscall.close (so2) < 0) 92 UnixMarshal.ThrowExceptionForLastError (); 93 } 94 } finally { 95 if (Syscall.close (so1) < 0) 96 UnixMarshal.ThrowExceptionForLastError (); 97 } 98 } 99 100 [Test] TestSocket()101 public void TestSocket () 102 { 103 int socket; 104 if ((socket = Syscall.socket (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0)) < 0) 105 UnixMarshal.ThrowExceptionForLastError (); 106 107 if (Syscall.close (socket) < 0) 108 UnixMarshal.ThrowExceptionForLastError (); 109 } 110 111 [Test] SocketPair()112 public void SocketPair () 113 { 114 int socket1, socket2; 115 if (Syscall.socketpair (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, out socket1, out socket2) < 0) 116 UnixMarshal.ThrowExceptionForLastError (); 117 118 int r0 = Syscall.close (socket1); 119 int r1 = Syscall.close (socket2); 120 if (r0 < 0 || r1 < 0) 121 UnixMarshal.ThrowExceptionForLastError (); 122 } 123 124 [Test] SendRecv()125 public void SendRecv () 126 { 127 WithSocketPair ((so1, so2) => { 128 long ret; 129 var buffer1 = new byte[] { 42, 43, 44 }; 130 ret = Syscall.send (so1, buffer1, (ulong) buffer1.Length, 0); 131 if (ret < 0) 132 UnixMarshal.ThrowExceptionForLastError (); 133 134 var buffer2 = new byte[1024]; 135 ret = Syscall.recv (so2, buffer2, (ulong) buffer2.Length, 0); 136 if (ret < 0) 137 UnixMarshal.ThrowExceptionForLastError (); 138 139 Assert.AreEqual (buffer1.Length, ret); 140 for (int i = 0; i < buffer1.Length; i++) 141 Assert.AreEqual (buffer1[i], buffer2[i]); 142 }); 143 } 144 145 [Test] SockOpt()146 public void SockOpt () 147 { 148 WithSockets (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { 149 int value; 150 if (Syscall.getsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_REUSEADDR, out value) < 0) 151 UnixMarshal.ThrowExceptionForLastError (); 152 Assert.AreEqual (0, value); 153 154 // Set SO_REUSEADDR to 1 155 if (Syscall.setsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_REUSEADDR, 1) < 0) 156 UnixMarshal.ThrowExceptionForLastError (); 157 158 // Get and check SO_REUSEADDR 159 if (Syscall.getsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_REUSEADDR, out value) < 0) 160 UnixMarshal.ThrowExceptionForLastError (); 161 Assert.AreNotEqual (0, value); 162 163 // Set SO_REUSEADDR to 0 164 if (Syscall.setsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_REUSEADDR, new byte[10], 4) < 0) 165 UnixMarshal.ThrowExceptionForLastError (); 166 167 // Get and check SO_REUSEADDR 168 var buffer = new byte[15]; 169 long size = 12; 170 if (Syscall.getsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_REUSEADDR, buffer, ref size) < 0) 171 UnixMarshal.ThrowExceptionForLastError (); 172 Assert.AreEqual (size, 4); 173 for (int i = 0; i < size; i++) 174 Assert.AreEqual (buffer[i], 0); 175 }); 176 } 177 178 [Test] 179 #if MONODROID 180 [ExpectedException (typeof (ArgumentOutOfRangeException))] // IPPROTO_TCP not supported 181 #endif SockOptLinger()182 public void SockOptLinger () 183 { 184 WithSockets (UnixAddressFamily.AF_INET, UnixSocketType.SOCK_STREAM, UnixSocketProtocol.IPPROTO_TCP, (so1, so2) => { 185 Linger linger = new Linger { 186 l_onoff = 1, 187 l_linger = 42, 188 }; 189 // Set SO_LINGER 190 if (Syscall.setsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_LINGER, linger) < 0) 191 UnixMarshal.ThrowExceptionForLastError (); 192 193 // Get and check SO_LINGER 194 Linger value; 195 if (Syscall.getsockopt (so1, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_LINGER, out value) < 0) 196 UnixMarshal.ThrowExceptionForLastError (); 197 if (value.l_onoff == 0) 198 Assert.Fail ("Linger not enabled"); 199 Assert.AreEqual (linger.l_linger, value.l_linger); 200 }); 201 } 202 203 [Test] Shutdown()204 public void Shutdown () 205 { 206 WithSocketPair ((so1, so2) => { 207 if (Syscall.shutdown (so1, ShutdownOption.SHUT_WR) < 0) 208 UnixMarshal.ThrowExceptionForLastError (); 209 210 var buffer2 = new byte[1024]; 211 long ret = Syscall.recv (so2, buffer2, (ulong) buffer2.Length, 0); 212 if (ret < 0) 213 UnixMarshal.ThrowExceptionForLastError (); 214 215 Assert.AreEqual (ret, 0); 216 }); 217 } 218 219 [Test] ByteOrder()220 public unsafe void ByteOrder () 221 { 222 ushort val1 = Syscall.htons (0x1234); 223 byte* ptr1 = (byte*) &val1; 224 Assert.AreEqual (ptr1[0], 0x12); 225 Assert.AreEqual (ptr1[1], 0x34); 226 227 uint val2 = Syscall.htonl (0x6789abcd); 228 byte* ptr2 = (byte*) &val2; 229 Assert.AreEqual (ptr2[0], 0x67); 230 Assert.AreEqual (ptr2[1], 0x89); 231 Assert.AreEqual (ptr2[2], 0xab); 232 Assert.AreEqual (ptr2[3], 0xcd); 233 234 ptr1[0] = 0xfe; 235 ptr1[1] = 0xdc; 236 Assert.AreEqual (Syscall.ntohs (val1), 0xfedc); 237 238 ptr2[0] = 0x76; 239 ptr2[1] = 0x54; 240 ptr2[2] = 0x32; 241 ptr2[3] = 0x10; 242 Assert.AreEqual (Syscall.ntohl (val2), 0x76543210); 243 } 244 245 [Test] InAddr()246 public void InAddr () 247 { 248 var ip = IPAddress.Loopback; 249 var inAddr = NativeConvert.ToInAddr (ip); 250 Assert.AreEqual (ip, NativeConvert.ToIPAddress (inAddr)); 251 Assert.AreEqual (0x7f000001, Syscall.ntohl (inAddr.s_addr)); 252 253 Assert.AreEqual ("127.0.0.1", inAddr.ToString ()); 254 } 255 256 [Test] In6Addr()257 public void In6Addr () 258 { 259 if (!Socket.OSSupportsIPv6) 260 Assert.Ignore ("OS does not support IPv6."); 261 262 var ip6 = IPAddress.IPv6Loopback; 263 var in6Addr = NativeConvert.ToIn6Addr (ip6); 264 Assert.AreEqual (ip6, NativeConvert.ToIPAddress (in6Addr)); 265 Assert.AreEqual (1, in6Addr[15]); 266 267 Assert.AreEqual ("::1", in6Addr.ToString ()); 268 } 269 270 [Test] SockaddrUnTest()271 public void SockaddrUnTest () 272 { 273 var address1 = new SockaddrUn ("/tmp/foo"); 274 Assert.AreEqual (address1.Path, "/tmp/foo"); 275 Assert.IsFalse (address1.IsLinuxAbstractNamespace); 276 277 var storage = address1.ToSockaddrStorage (); 278 var address2 = SockaddrUn.FromSockaddrStorage (storage); 279 Assert.AreEqual (address1, address2); 280 281 var sockaddr = Sockaddr.FromSockaddrStorage (storage); 282 Assert.AreEqual (sockaddr.sa_family, address1.sa_family); 283 284 var address3 = new SockaddrUn ("/tmp/bar", linuxAbstractNamespace:true); 285 Assert.AreEqual (address3.Path, "/tmp/bar"); 286 Assert.IsTrue (address3.IsLinuxAbstractNamespace); 287 288 var address4 = new SockaddrUn (new string ('X', 9000)); 289 Assert.AreEqual (address4.Path, new string ('X', 9000)); 290 Assert.IsFalse (address4.IsLinuxAbstractNamespace); 291 var storage2 = address4.ToSockaddrStorage (); 292 var address5 = SockaddrUn.FromSockaddrStorage (storage2); 293 Assert.AreEqual (address4, address5); 294 // Test the malloc() path for long SockaddrUn adresses (the syscalls will fail because the fd is invalid and because the path is too long) 295 Syscall.bind (-1, address4); 296 Syscall.getsockname (-1, address4); 297 298 Assert.AreEqual ("{sa_family=AF_UNIX, sun_path=\"/tmp/foo\"}", address1.ToString ()); 299 Assert.AreEqual ("{sa_family=AF_UNIX, sun_path=\"\\0/tmp/bar\"}", address3.ToString ()); 300 } 301 302 [Test] SockaddrInTest()303 public void SockaddrInTest () 304 { 305 var address1 = new SockaddrIn { 306 sin_family = UnixAddressFamily.AF_INET, 307 sin_port = Syscall.htons (5678), 308 sin_addr = NativeConvert.ToInAddr (IPAddress.Loopback), 309 }; 310 311 var storage = address1.ToSockaddrStorage (); 312 var address2 = SockaddrIn.FromSockaddrStorage (storage); 313 Assert.AreEqual (address1, address2); 314 315 var sockaddr = Sockaddr.FromSockaddrStorage (storage); 316 Assert.AreEqual (sockaddr.sa_family, address1.sa_family); 317 318 var storage2 = storage.ToSockaddrStorage (); 319 Assert.AreEqual (storage, storage2); 320 321 var storage3 = new SockaddrStorage (123); 322 storage2.CopyTo (storage3); 323 Assert.AreEqual (storage, storage3); 324 325 Assert.AreEqual ("{sin_family=AF_INET, sin_port=htons(5678), sin_addr=127.0.0.1}", address1.ToString ()); 326 } 327 328 [Test] SockaddrIn6Test()329 public void SockaddrIn6Test () 330 { 331 if (!Socket.OSSupportsIPv6) 332 Assert.Ignore ("OS does not support IPv6."); 333 334 var address1 = new SockaddrIn6 { 335 sin6_family = UnixAddressFamily.AF_INET6, 336 sin6_port = Syscall.htons (1234), 337 sin6_flowinfo = 2, 338 sin6_addr = NativeConvert.ToIn6Addr (IPAddress.IPv6Loopback), 339 sin6_scope_id = 3 340 }; 341 342 var storage = address1.ToSockaddrStorage (); 343 var address2 = SockaddrIn6.FromSockaddrStorage (storage); 344 Assert.AreEqual (address1, address2); 345 346 var sockaddr = Sockaddr.FromSockaddrStorage (storage); 347 Assert.AreEqual (sockaddr.sa_family, address1.sa_family); 348 349 Assert.AreEqual ("{sin6_family=AF_INET6, sin6_port=htons(1234), sin6_flowinfo=2, sin6_addr=::1, sin6_scope_id=3}", address1.ToString ()); 350 } 351 352 [Test] 353 #if MONODROID 354 [ExpectedException (typeof (ArgumentOutOfRangeException))] // IPPROTO_UDP not supported 355 #endif BindConnect()356 public void BindConnect () 357 { 358 WithSockets (UnixAddressFamily.AF_INET, UnixSocketType.SOCK_DGRAM, UnixSocketProtocol.IPPROTO_UDP, (so1, so2) => { 359 // Bind UDP socket so1 to 127.0.0.1 with dynamic port 360 var address = new SockaddrIn { 361 sin_family = UnixAddressFamily.AF_INET, 362 sin_port = Syscall.htons (0), 363 sin_addr = new InAddr (127, 0, 0, 1), 364 }; 365 if (Syscall.bind (so1, address) < 0) 366 UnixMarshal.ThrowExceptionForLastError (); 367 368 // Get actual port number using getsockname() 369 var actualAddress = new SockaddrIn (); 370 if (Syscall.getsockname (so1, actualAddress) < 0) 371 UnixMarshal.ThrowExceptionForLastError (); 372 Assert.AreEqual (actualAddress.sa_family, UnixAddressFamily.AF_INET); 373 var port = Syscall.ntohs (actualAddress.sin_port); 374 Assert.IsTrue (port != 0); 375 376 377 // Connect so2 to so1 378 var remoteAddress = new SockaddrIn { 379 sin_family = UnixAddressFamily.AF_INET, 380 sin_port = Syscall.htons (port), 381 sin_addr = new InAddr (127, 0, 0, 1), 382 }; 383 if (Syscall.connect (so2, remoteAddress) < 0) 384 UnixMarshal.ThrowExceptionForLastError (); 385 386 // Verify peer address using getpeername() 387 var address2 = new SockaddrIn (); 388 if (Syscall.getpeername (so2, address2) < 0) 389 UnixMarshal.ThrowExceptionForLastError (); 390 Assert.AreEqual (address2.sa_family, UnixAddressFamily.AF_INET); 391 Assert.AreEqual (remoteAddress.sin_port, address2.sin_port); 392 Assert.AreEqual (remoteAddress.sin_addr, address2.sin_addr); 393 394 // Send and receive a few bytes 395 long ret; 396 var buffer1 = new byte[] { 42, 43, 44 }; 397 ret = Syscall.send (so2, buffer1, (ulong) buffer1.Length, 0); 398 if (ret < 0) 399 UnixMarshal.ThrowExceptionForLastError (); 400 401 var buffer2 = new byte[1024]; 402 ret = Syscall.recv (so1, buffer2, (ulong) buffer2.Length, 0); 403 if (ret < 0) 404 UnixMarshal.ThrowExceptionForLastError (); 405 406 Assert.AreEqual (buffer1.Length, ret); 407 for (int i = 0; i < buffer1.Length; i++) 408 Assert.AreEqual (buffer1[i], buffer2[i]); 409 }); 410 } 411 412 [Test] IPv6()413 public void IPv6 () 414 { 415 if (!Socket.OSSupportsIPv6) 416 Assert.Ignore ("OS does not support IPv6."); 417 418 var address = new SockaddrIn6 { 419 sin6_family = UnixAddressFamily.AF_INET6, 420 sin6_port = Syscall.htons (0), 421 sin6_addr = NativeConvert.ToIn6Addr (IPAddress.IPv6Loopback), 422 }; 423 WithSockets (UnixAddressFamily.AF_INET6, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { 424 if (Syscall.bind (so1, address) < 0) 425 UnixMarshal.ThrowExceptionForLastError (); 426 427 var address1Stor = new SockaddrStorage (); 428 if (Syscall.getsockname (so1, address1Stor) < 0) 429 UnixMarshal.ThrowExceptionForLastError (); 430 var address1 = new SockaddrIn6 (); 431 address1Stor.CopyTo (address1); 432 433 // Check getsockname(socket, null) 434 if (Syscall.getsockname (so1, null) < 0) 435 UnixMarshal.ThrowExceptionForLastError (); 436 437 var address2 = new SockaddrIn6 (); 438 if (Syscall.getsockname (so1, address2) < 0) 439 UnixMarshal.ThrowExceptionForLastError (); 440 441 Assert.AreEqual (address1, address2); 442 Assert.IsTrue (Syscall.ntohs (address1.sin6_port) != 0); 443 address1.sin6_port = 0; 444 Assert.AreEqual (address, address1); 445 446 var address3 = new Sockaddr (); 447 if (Syscall.getsockname (so1, address3) < 0) 448 UnixMarshal.ThrowExceptionForLastError (); 449 Assert.AreEqual (address.sa_family, address3.sa_family); 450 451 // Try to store a sockaddr_in6 into a Sockaddr. Should fail because sockaddr_in6 should be larger than sockaddr_in 452 var address4 = new SockaddrIn (); 453 if (Syscall.getsockname (so1, address4) == 0) 454 Assert.Fail ("getsockname() should have failed"); 455 Assert.AreEqual (Errno.ENOBUFS, Stdlib.GetLastError ()); 456 }); 457 } 458 459 [Test] UnixAccept()460 public void UnixAccept () 461 { 462 var address = new SockaddrUn (TempFolder + "/socket1"); 463 var address2 = SockaddrUn.FromSockaddrStorage (address.ToSockaddrStorage ()); 464 Assert.AreEqual (address, address2); 465 466 WithSockets (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { 467 if (Syscall.bind (so1, address) < 0) 468 UnixMarshal.ThrowExceptionForLastError (); 469 470 if (Syscall.listen (so1, 5) < 0) 471 UnixMarshal.ThrowExceptionForLastError (); 472 473 if (Syscall.connect (so2, address) < 0) 474 UnixMarshal.ThrowExceptionForLastError (); 475 476 var address3 = new SockaddrUn (); 477 if (Syscall.getsockname (so1, address3) < 0) 478 UnixMarshal.ThrowExceptionForLastError (); 479 Assert.AreEqual (address, address3); 480 481 var address4 = new SockaddrStorage (); 482 if (Syscall.getsockname (so1, address4) < 0) 483 UnixMarshal.ThrowExceptionForLastError (); 484 Assert.AreEqual (UnixAddressFamily.AF_UNIX, address4.sa_family); 485 Assert.AreEqual (address3, SockaddrUn.FromSockaddrStorage (address4)); 486 487 var address5 = new SockaddrUn (); 488 if (Syscall.getsockname (so1, address5) < 0) 489 UnixMarshal.ThrowExceptionForLastError (); 490 Assert.AreEqual (UnixAddressFamily.AF_UNIX, address5.sa_family); 491 492 // Check getsockname(socket, null) 493 if (Syscall.getsockname (so1, null) < 0) 494 UnixMarshal.ThrowExceptionForLastError (); 495 496 int so3; 497 var remote = new SockaddrUn (); 498 if ((so3 = Syscall.accept (so1, remote)) < 0) 499 UnixMarshal.ThrowExceptionForLastError (); 500 try { 501 // Send and receive a few bytes 502 long ret; 503 var buffer1 = new byte[] { 42, 43, 44 }; 504 ret = Syscall.send (so2, buffer1, (ulong) buffer1.Length, 0); 505 if (ret < 0) 506 UnixMarshal.ThrowExceptionForLastError (); 507 508 var buffer2 = new byte[1024]; 509 ret = Syscall.recv (so3, buffer2, (ulong) buffer2.Length, 0); 510 if (ret < 0) 511 UnixMarshal.ThrowExceptionForLastError (); 512 513 Assert.AreEqual (buffer1.Length, ret); 514 for (int i = 0; i < buffer1.Length; i++) 515 Assert.AreEqual (buffer1[i], buffer2[i]); 516 } finally { 517 if (Syscall.close (so3) < 0) 518 UnixMarshal.ThrowExceptionForLastError (); 519 } 520 }); 521 } 522 523 [Test] 524 [Category ("NotOnMac")] 525 #if MONODROID 526 [ExpectedException (typeof (ArgumentOutOfRangeException))] // SOCK_NONBLOCK, SOCK_CLOEXEC not supported 527 #endif Accept4()528 public void Accept4 () 529 { 530 WithSockets (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { 531 var address = new SockaddrUn (TempFolder + "/socket2"); 532 if (Syscall.bind (so1, address) < 0) 533 UnixMarshal.ThrowExceptionForLastError (); 534 if (Syscall.listen (so1, 5) < 0) 535 UnixMarshal.ThrowExceptionForLastError (); 536 if (Syscall.connect (so2, address) < 0) 537 UnixMarshal.ThrowExceptionForLastError (); 538 539 int so3; 540 var remote = new SockaddrUn (); 541 if ((so3 = Syscall.accept4 (so1, remote, UnixSocketFlags.SOCK_CLOEXEC | UnixSocketFlags.SOCK_NONBLOCK)) < 0) 542 UnixMarshal.ThrowExceptionForLastError (); 543 try { 544 int _flags; 545 if ((_flags = Syscall.fcntl (so3, FcntlCommand.F_GETFL)) < 0) 546 UnixMarshal.ThrowExceptionForLastError (); 547 var flags = NativeConvert.ToOpenFlags (_flags); 548 Assert.IsTrue ((flags & OpenFlags.O_NONBLOCK) != 0); 549 550 int _flagsFD; 551 if ((_flagsFD = Syscall.fcntl (so3, FcntlCommand.F_GETFD)) < 0) 552 UnixMarshal.ThrowExceptionForLastError (); 553 // FD_CLOEXEC must be set 554 //var flagsFD = NativeConvert.ToFdFlags (_flagsFD); 555 //Assert.IsTrue ((flagsFD & FdFlags.FD_CLOEXEC) != 0); 556 Assert.IsTrue (_flagsFD != 0); 557 } finally { 558 if (Syscall.close (so3) < 0) 559 UnixMarshal.ThrowExceptionForLastError (); 560 } 561 }); 562 } 563 564 [Test] 565 #if MONODROID 566 [ExpectedException (typeof (ArgumentOutOfRangeException))] // IPPROTO_UDP not supported 567 #endif SendToRecvFrom()568 public void SendToRecvFrom () 569 { 570 WithSockets (UnixAddressFamily.AF_INET, UnixSocketType.SOCK_DGRAM, UnixSocketProtocol.IPPROTO_UDP, (so1, so2) => { 571 // Bind UDP socket so1 to 127.0.0.1 with dynamic port 572 var address = new SockaddrIn { sin_family = UnixAddressFamily.AF_INET, sin_port = Syscall.htons (0), sin_addr = new InAddr (127, 0, 0, 1) }; 573 if (Syscall.bind (so1, address) < 0) 574 UnixMarshal.ThrowExceptionForLastError (); 575 576 // Get actual port number using getsockname() 577 var actualAddress = new SockaddrIn (); 578 if (Syscall.getsockname (so1, actualAddress) < 0) 579 UnixMarshal.ThrowExceptionForLastError (); 580 Assert.AreEqual (actualAddress.sa_family, UnixAddressFamily.AF_INET); 581 var port = Syscall.ntohs (actualAddress.sin_port); 582 Assert.IsTrue (port != 0); 583 584 585 var remoteAddress = new SockaddrIn { 586 sin_family = UnixAddressFamily.AF_INET, 587 sin_port = Syscall.htons (port), 588 sin_addr = new InAddr (127, 0, 0, 1), 589 }; 590 591 // Send and receive a few bytes 592 long ret; 593 var buffer1 = new byte[] { 42, 43, 44 }; 594 ret = Syscall.sendto (so2, buffer1, (ulong) buffer1.Length, 0, remoteAddress); 595 if (ret < 0) 596 UnixMarshal.ThrowExceptionForLastError (); 597 598 var senderAddress = new SockaddrIn (); 599 var buffer2 = new byte[1024]; 600 ret = Syscall.recvfrom (so1, buffer2, (ulong) buffer2.Length, 0, senderAddress); 601 if (ret < 0) 602 UnixMarshal.ThrowExceptionForLastError (); 603 Assert.AreEqual (senderAddress.sa_family, UnixAddressFamily.AF_INET); 604 Assert.AreEqual (senderAddress.sin_addr, new InAddr (127, 0, 0, 1)); 605 606 Assert.AreEqual (buffer1.Length, ret); 607 for (int i = 0; i < buffer1.Length; i++) 608 Assert.AreEqual (buffer1[i], buffer2[i]); 609 }); 610 } 611 612 [Test] SendMsgRecvMsg()613 public unsafe void SendMsgRecvMsg () 614 { 615 WithSocketPair ((so1, so2) => { 616 long ret; 617 var buffer1 = new byte[] { 42, 43, 44 }; 618 fixed (byte* ptr_buffer1 = buffer1) { 619 var iovecs1 = new Iovec[] { 620 new Iovec { 621 iov_base = (IntPtr) ptr_buffer1, 622 iov_len = (ulong) buffer1.Length, 623 }, 624 }; 625 var msghdr1 = new Msghdr { 626 msg_iov = iovecs1, 627 msg_iovlen = 1, 628 }; 629 ret = Syscall.sendmsg (so1, msghdr1, 0); 630 } 631 if (ret < 0) 632 UnixMarshal.ThrowExceptionForLastError (); 633 634 var buffer2 = new byte[1024]; 635 fixed (byte* ptr_buffer2 = buffer2) { 636 var iovecs2 = new Iovec[] { 637 new Iovec { 638 iov_base = (IntPtr) ptr_buffer2, 639 iov_len = (ulong) buffer2.Length, 640 }, 641 }; 642 var msghdr2 = new Msghdr { 643 msg_iov = iovecs2, 644 msg_iovlen = 1, 645 }; 646 ret = Syscall.recvmsg (so2, msghdr2, 0); 647 } 648 if (ret < 0) 649 UnixMarshal.ThrowExceptionForLastError (); 650 651 Assert.AreEqual (buffer1.Length, ret); 652 for (int i = 0; i < buffer1.Length; i++) 653 Assert.AreEqual (buffer1[i], buffer2[i]); 654 }); 655 } 656 657 [Test] 658 #if MONODROID 659 [ExpectedException (typeof (ArgumentOutOfRangeException))] // IPPROTO_UDP not supported 660 #endif SendMsgRecvMsgAddress()661 public unsafe void SendMsgRecvMsgAddress () 662 { 663 WithSockets (UnixAddressFamily.AF_INET, UnixSocketType.SOCK_DGRAM, UnixSocketProtocol.IPPROTO_UDP, (so1, so2) => { 664 // Bind UDP socket so1 to 127.0.0.1 with dynamic port 665 var address = new SockaddrIn { 666 sin_family = UnixAddressFamily.AF_INET, 667 sin_port = Syscall.htons (0), 668 sin_addr = new InAddr (127, 0, 0, 1), 669 }; 670 if (Syscall.bind (so1, address) < 0) 671 UnixMarshal.ThrowExceptionForLastError (); 672 673 // Get actual port number using getsockname() 674 var actualAddress = new SockaddrIn (); 675 if (Syscall.getsockname (so1, actualAddress) < 0) 676 UnixMarshal.ThrowExceptionForLastError (); 677 Assert.AreEqual (actualAddress.sa_family, UnixAddressFamily.AF_INET); 678 var port = Syscall.ntohs (actualAddress.sin_port); 679 Assert.IsTrue (port != 0); 680 681 682 var remoteAddress = new SockaddrIn { 683 sin_family = UnixAddressFamily.AF_INET, 684 sin_port = Syscall.htons (port), 685 sin_addr = new InAddr (127, 0, 0, 1), 686 }; 687 688 // Send and receive a few bytes 689 long ret; 690 var buffer1 = new byte[] { 42, 43, 44 }; 691 fixed (byte* ptr_buffer1 = buffer1) { 692 var iovecs1 = new Iovec[] { 693 new Iovec { 694 iov_base = (IntPtr) ptr_buffer1, 695 iov_len = (ulong) buffer1.Length, 696 }, 697 }; 698 var msghdr1 = new Msghdr { 699 msg_name = remoteAddress, 700 msg_iov = iovecs1, 701 msg_iovlen = 1, 702 }; 703 ret = Syscall.sendmsg (so2, msghdr1, 0); 704 msghdr1.msg_name = remoteAddress.ToSockaddrStorage (); 705 if (ret >= 0) 706 ret = Syscall.sendmsg (so2, msghdr1, 0); 707 } 708 if (ret < 0) 709 UnixMarshal.ThrowExceptionForLastError (); 710 711 var senderAddress = new SockaddrIn (); 712 var senderAddressStorage = new SockaddrStorage (); 713 var buffer2 = new byte[1024]; 714 var buffer3 = new byte[1024]; 715 fixed (byte* ptr_buffer2 = buffer2, ptr_buffer3 = buffer3) { 716 var iovecs2 = new Iovec[] { 717 new Iovec { 718 iov_base = (IntPtr) ptr_buffer2, 719 iov_len = (ulong) buffer2.Length, 720 }, 721 }; 722 var msghdr2 = new Msghdr { 723 msg_name = senderAddress, 724 msg_iov = iovecs2, 725 msg_iovlen = 1, 726 }; 727 ret = Syscall.recvmsg (so1, msghdr2, 0); 728 msghdr2.msg_name = senderAddressStorage; 729 iovecs2[0].iov_base = (IntPtr) ptr_buffer3; 730 if (ret >= 0) 731 ret = Syscall.recvmsg (so1, msghdr2, 0); 732 } 733 if (ret < 0) 734 UnixMarshal.ThrowExceptionForLastError (); 735 Assert.AreEqual (senderAddress.sa_family, UnixAddressFamily.AF_INET); 736 Assert.AreEqual (senderAddress.sin_addr, new InAddr (127, 0, 0, 1)); 737 var senderAddress2 = SockaddrIn.FromSockaddrStorage (senderAddressStorage); 738 Assert.AreEqual (senderAddress2.sa_family, UnixAddressFamily.AF_INET); 739 Assert.AreEqual (senderAddress2.sin_addr, new InAddr (127, 0, 0, 1)); 740 741 Assert.AreEqual (buffer1.Length, ret); 742 for (int i = 0; i < buffer1.Length; i++) 743 Assert.AreEqual (buffer1[i], buffer2[i]); 744 for (int i = 0; i < buffer1.Length; i++) 745 Assert.AreEqual (buffer1[i], buffer3[i]); 746 }); 747 } 748 ControlMsg(bool useMultipleControlMessages)749 public unsafe void ControlMsg (bool useMultipleControlMessages) 750 { 751 // Create two socket pairs and send inner_so1 and inner_so2 over the other socket pair using SCM_RIGHTS 752 WithSocketPair ((inner_so1, inner_so2) => { 753 WithSocketPair ((so1, so2) => { 754 byte[] cmsg; 755 Msghdr msghdr1; 756 long offset; 757 if (useMultipleControlMessages) { 758 // Create two SCM_RIGHTS control messages 759 cmsg = new byte[2 * Syscall.CMSG_SPACE (sizeof (int))]; 760 var hdr = new Cmsghdr { 761 cmsg_len = (long) Syscall.CMSG_LEN (sizeof (int)), 762 cmsg_level = UnixSocketProtocol.SOL_SOCKET, 763 cmsg_type = UnixSocketControlMessage.SCM_RIGHTS, 764 }; 765 msghdr1 = new Msghdr { 766 msg_control = cmsg, 767 msg_controllen = cmsg.Length, 768 }; 769 offset = 0; 770 hdr.WriteToBuffer (msghdr1, offset); 771 var dataOffset = Syscall.CMSG_DATA (msghdr1, offset); 772 fixed (byte* ptr = msghdr1.msg_control) { 773 ((int*) (ptr + dataOffset))[0] = inner_so1; 774 } 775 offset = (long) Syscall.CMSG_SPACE (sizeof (int)); 776 hdr.WriteToBuffer (msghdr1, offset); 777 dataOffset = Syscall.CMSG_DATA (msghdr1, offset); 778 fixed (byte* ptr = msghdr1.msg_control) { 779 ((int*) (ptr + dataOffset))[0] = inner_so2; 780 } 781 } else { 782 // Create one SCM_RIGHTS control message 783 cmsg = new byte[Syscall.CMSG_SPACE (2 * sizeof (int))]; 784 var hdr = new Cmsghdr { 785 cmsg_len = (long) Syscall.CMSG_LEN (2 * sizeof (int)), 786 cmsg_level = UnixSocketProtocol.SOL_SOCKET, 787 cmsg_type = UnixSocketControlMessage.SCM_RIGHTS, 788 }; 789 msghdr1 = new Msghdr { 790 msg_control = cmsg, 791 msg_controllen = cmsg.Length, 792 }; 793 offset = 0; 794 hdr.WriteToBuffer (msghdr1, offset); 795 var dataOffset = Syscall.CMSG_DATA (msghdr1, offset); 796 fixed (byte* ptr = msghdr1.msg_control) { 797 ((int*) (ptr + dataOffset))[0] = inner_so1; 798 ((int*) (ptr + dataOffset))[1] = inner_so2; 799 } 800 } 801 802 long ret; 803 var buffer1 = new byte[] { 42, 43, 44 }; 804 fixed (byte* ptr_buffer1 = buffer1) { 805 var iovecs1 = new Iovec[] { 806 new Iovec { 807 iov_base = (IntPtr) ptr_buffer1, 808 iov_len = (ulong) buffer1.Length, 809 }, 810 }; 811 msghdr1.msg_iov = iovecs1; 812 msghdr1.msg_iovlen = 1; 813 // Send message twice 814 ret = Syscall.sendmsg (so1, msghdr1, 0); 815 if (ret < 0) 816 UnixMarshal.ThrowExceptionForLastError (); 817 ret = Syscall.sendmsg (so1, msghdr1, 0); 818 if (ret < 0) 819 UnixMarshal.ThrowExceptionForLastError (); 820 } 821 822 // Receive without control message buffer 823 var buffer2 = new byte[1024]; 824 var msghdr2 = new Msghdr { }; 825 fixed (byte* ptr_buffer2 = buffer2) { 826 var iovecs2 = new Iovec[] { 827 new Iovec { 828 iov_base = (IntPtr) ptr_buffer2, 829 iov_len = (ulong) buffer2.Length, 830 }, 831 }; 832 msghdr2.msg_iov = iovecs2; 833 msghdr2.msg_iovlen = 1; 834 ret = Syscall.recvmsg (so2, msghdr2, 0); 835 } 836 if (ret < 0) 837 UnixMarshal.ThrowExceptionForLastError (); 838 839 if (useMultipleControlMessages) // This assertion fails on OSX for some reason 840 Assert.IsTrue ((msghdr2.msg_flags & MessageFlags.MSG_CTRUNC) != 0); // Control message has been truncated 841 842 Assert.AreEqual (buffer1.Length, ret); 843 for (int i = 0; i < buffer1.Length; i++) 844 Assert.AreEqual (buffer1[i], buffer2[i]); 845 846 // Receive with control message buffer 847 buffer2 = new byte[1024]; 848 var cmsg2 = new byte[1024]; 849 msghdr2 = new Msghdr { 850 msg_control = cmsg2, 851 msg_controllen = cmsg2.Length, 852 }; 853 fixed (byte* ptr_buffer2 = buffer2) { 854 var iovecs2 = new Iovec[] { 855 new Iovec { 856 iov_base = (IntPtr) ptr_buffer2, 857 iov_len = (ulong) buffer2.Length, 858 }, 859 }; 860 msghdr2.msg_iov = iovecs2; 861 msghdr2.msg_iovlen = 1; 862 ret = Syscall.recvmsg (so2, msghdr2, 0); 863 } 864 if (ret < 0) 865 UnixMarshal.ThrowExceptionForLastError (); 866 867 var fds = new global::System.Collections.Generic.List<int> (); 868 for (offset = Syscall.CMSG_FIRSTHDR (msghdr2); offset != -1; offset = Syscall.CMSG_NXTHDR (msghdr2, offset)) { 869 var recvHdr = Cmsghdr.ReadFromBuffer (msghdr2, offset); 870 var recvDataOffset = Syscall.CMSG_DATA (msghdr2, offset); 871 var bytes = recvHdr.cmsg_len - (recvDataOffset - offset); 872 Assert.AreEqual (bytes % sizeof (int), 0); 873 var fdCount = bytes / sizeof (int); 874 fixed (byte* ptr = msghdr2.msg_control) 875 for (int i = 0; i < fdCount; i++) 876 fds.Add (((int*) (ptr + recvDataOffset))[i]); 877 } 878 try { 879 Assert.IsTrue ((msghdr2.msg_flags & MessageFlags.MSG_CTRUNC) == 0); // Control message has not been truncated 880 881 Assert.AreEqual (buffer1.Length, ret); 882 for (int i = 0; i < buffer1.Length; i++) 883 Assert.AreEqual (buffer1[i], buffer2[i]); 884 885 Assert.AreEqual (fds.Count, 2); 886 887 // Send message over the first received fd and receive it over inner_so2 888 var buffer3 = new byte[] { 16, 17 }; 889 ret = Syscall.send (fds[0], buffer3, (ulong) buffer3.Length, 0); 890 if (ret < 0) 891 UnixMarshal.ThrowExceptionForLastError (); 892 893 var buffer4 = new byte[1024]; 894 ret = Syscall.recv (inner_so2, buffer4, (ulong) buffer4.Length, 0); 895 if (ret < 0) 896 UnixMarshal.ThrowExceptionForLastError (); 897 898 Assert.AreEqual (buffer3.Length, ret); 899 for (int i = 0; i < buffer3.Length; i++) 900 Assert.AreEqual (buffer3[i], buffer4[i]); 901 902 // Send message over inner_so1 and receive it second received fd 903 var buffer5 = new byte[] { 10, 40, 0, 1 }; 904 ret = Syscall.send (inner_so1, buffer5, (ulong) buffer5.Length, 0); 905 if (ret < 0) 906 UnixMarshal.ThrowExceptionForLastError (); 907 908 var buffer6 = new byte[1024]; 909 ret = Syscall.recv (fds[1], buffer6, (ulong) buffer6.Length, 0); 910 if (ret < 0) 911 UnixMarshal.ThrowExceptionForLastError (); 912 913 Assert.AreEqual (buffer5.Length, ret); 914 for (int i = 0; i < buffer5.Length; i++) 915 Assert.AreEqual (buffer5[i], buffer6[i]); 916 } finally { 917 foreach (var fd in fds) 918 if (Syscall.close (fd) < 0) 919 UnixMarshal.ThrowExceptionForLastError (); 920 } 921 }); 922 }); 923 } 924 925 [Test] ControlMsgOneCmsg()926 public unsafe void ControlMsgOneCmsg () 927 { 928 ControlMsg (useMultipleControlMessages: false); 929 } 930 931 [Test] 932 [Category ("NotOnMac")] ControlMsgMultipleCMsgs()933 public unsafe void ControlMsgMultipleCMsgs () 934 { 935 ControlMsg (useMultipleControlMessages: true); 936 } 937 } 938 } 939 940 // vim: noexpandtab 941 // Local Variables: 942 // tab-width: 4 943 // c-basic-offset: 4 944 // indent-tabs-mode: t 945 // End: 946