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