+static int
+get_addrlen (struct Mono_Posix__SockaddrHeader* address, socklen_t* addrlen)
+{
+ if (!address) {
+ *addrlen = 0;
+ return 0;
+ }
+
+ switch (address->type) {
+ case Mono_Posix_SockaddrType_SockaddrStorage:
+ mph_return_if_socklen_t_overflow (((struct Mono_Posix__SockaddrDynamic*) address)->len);
+ *addrlen = ((struct Mono_Posix__SockaddrDynamic*) address)->len;
+ return 0;
+ case Mono_Posix_SockaddrType_SockaddrUn:
+ mph_return_if_socklen_t_overflow (offsetof (struct sockaddr_un, sun_path) + ((struct Mono_Posix__SockaddrDynamic*) address)->len);
+ *addrlen = offsetof (struct sockaddr_un, sun_path) + ((struct Mono_Posix__SockaddrDynamic*) address)->len;
+ return 0;
+ case Mono_Posix_SockaddrType_Sockaddr: *addrlen = sizeof (struct sockaddr); return 0;
+ case Mono_Posix_SockaddrType_SockaddrIn: *addrlen = sizeof (struct sockaddr_in); return 0;
+ case Mono_Posix_SockaddrType_SockaddrIn6: *addrlen = sizeof (struct sockaddr_in6); return 0;
+ default:
+ *addrlen = 0;
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+int
+Mono_Posix_Sockaddr_GetNativeSize (struct Mono_Posix__SockaddrHeader* address, gint64* size)
+{
+ socklen_t value;
+ int r;
+
+ r = get_addrlen (address, &value);
+ *size = value;
+ return r;
+}
+
+int
+Mono_Posix_FromSockaddr (struct Mono_Posix__SockaddrHeader* source, void* destination)
+{
+ if (!source)
+ return 0;
+
+ switch (source->type) {
+ case Mono_Posix_SockaddrType_SockaddrStorage:
+ // Do nothing, don't copy source->sa_family into addr->sa_family
+ return 0;
+
+ case Mono_Posix_SockaddrType_SockaddrUn:
+ memcpy (((struct sockaddr_un*) destination)->sun_path, ((struct Mono_Posix__SockaddrDynamic*) source)->data, ((struct Mono_Posix__SockaddrDynamic*) source)->len);
+ break;
+
+ case Mono_Posix_SockaddrType_Sockaddr:
+ break;
+
+ case Mono_Posix_SockaddrType_SockaddrIn:
+ if (Mono_Posix_FromSockaddrIn ((struct Mono_Posix_SockaddrIn*) source, (struct sockaddr_in*) destination) != 0)
+ return -1;
+ break;
+
+ case Mono_Posix_SockaddrType_SockaddrIn6:
+ if (Mono_Posix_FromSockaddrIn6 ((struct Mono_Posix_SockaddrIn6*) source, (struct sockaddr_in6*) destination) != 0)
+ return -1;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ int family;
+ if (Mono_Posix_FromUnixAddressFamily (source->sa_family, &family) != 0)
+ return -1;
+ ((struct sockaddr*) destination)->sa_family = family;
+
+ return 0;
+}
+
+int
+Mono_Posix_ToSockaddr (void* source, gint64 size, struct Mono_Posix__SockaddrHeader* destination)
+{
+ struct Mono_Posix__SockaddrDynamic* destination_dyn;
+
+ if (!destination)
+ return 0;
+
+ switch (destination->type) {
+ case Mono_Posix_SockaddrType_Sockaddr:
+ if (size < offsetof (struct sockaddr, sa_family) + sizeof (sa_family_t)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ break;
+
+ case Mono_Posix_SockaddrType_SockaddrStorage:
+ destination_dyn = ((struct Mono_Posix__SockaddrDynamic*) destination);
+ if (size > destination_dyn->len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ destination_dyn->len = size;
+ break;
+
+ case Mono_Posix_SockaddrType_SockaddrUn:
+ destination_dyn = ((struct Mono_Posix__SockaddrDynamic*) destination);
+ if (size - offsetof (struct sockaddr_un, sun_path) > destination_dyn->len) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ destination_dyn->len = size - offsetof (struct sockaddr_un, sun_path);
+ memcpy (destination_dyn->data, ((struct sockaddr_un*) source)->sun_path, size);
+ break;
+
+ case Mono_Posix_SockaddrType_SockaddrIn:
+ if (size != sizeof (struct sockaddr_in)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (Mono_Posix_ToSockaddrIn ((struct sockaddr_in*) source, (struct Mono_Posix_SockaddrIn*) destination) != 0)
+ return -1;
+ break;
+
+ case Mono_Posix_SockaddrType_SockaddrIn6:
+ if (size != sizeof (struct sockaddr_in6)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (Mono_Posix_ToSockaddrIn6 ((struct sockaddr_in6*) source, (struct Mono_Posix_SockaddrIn6*) destination) != 0)
+ return -1;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (Mono_Posix_ToUnixAddressFamily (((struct sockaddr*) source)->sa_family, &destination->sa_family) != 0)
+ destination->sa_family = Mono_Posix_UnixAddressFamily_Unknown;
+
+ return 0;
+}
+
+// Macro for allocating space for the native sockaddr_* structure
+// Must be a macro because it is using alloca()
+
+#define ALLOC_SOCKADDR \
+ socklen_t addrlen; \
+ struct sockaddr* addr; \
+ gboolean need_free = 0; \
+ \
+ if (get_addrlen (address, &addrlen) != 0) \
+ return -1; \
+ if (address == NULL) { \
+ addr = NULL; \
+ } else if (address->type == Mono_Posix_SockaddrType_SockaddrStorage) { \
+ addr = (struct sockaddr*) ((struct Mono_Posix__SockaddrDynamic*) address)->data; \
+ } else if (address->type == Mono_Posix_SockaddrType_SockaddrUn) { \
+ /* Use alloca() for up to 2048 bytes, use malloc() otherwise */ \
+ need_free = addrlen > 2048; \
+ addr = need_free ? malloc (addrlen) : alloca (addrlen); \
+ if (!addr) \
+ return -1; \
+ } else { \
+ addr = alloca (addrlen); \
+ }
+
+
+int
+Mono_Posix_Syscall_bind (int socket, struct Mono_Posix__SockaddrHeader* address)
+{
+ int r;
+
+ ALLOC_SOCKADDR
+ if (Mono_Posix_FromSockaddr (address, addr) != 0) {
+ if (need_free)
+ free (addr);
+ return -1;
+ }
+
+ r = bind (socket, addr, addrlen);
+
+ if (need_free)
+ free (addr);
+
+ return r;
+}
+
+int
+Mono_Posix_Syscall_connect (int socket, struct Mono_Posix__SockaddrHeader* address)
+{
+ int r;
+
+ ALLOC_SOCKADDR
+ if (Mono_Posix_FromSockaddr (address, addr) != 0) {
+ if (need_free)
+ free (addr);
+ return -1;
+ }
+
+ r = connect (socket, addr, addrlen);
+
+ if (need_free)
+ free (addr);
+
+ return r;
+}
+
+int
+Mono_Posix_Syscall_accept (int socket, struct Mono_Posix__SockaddrHeader* address)
+{
+ int r;
+
+ ALLOC_SOCKADDR
+
+ r = accept (socket, addr, &addrlen);
+
+ if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) {
+ close (r);
+ r = -1;
+ }
+
+ if (need_free)
+ free (addr);
+
+ return r;
+}
+
+#ifdef HAVE_ACCEPT4
+int
+Mono_Posix_Syscall_accept4 (int socket, struct Mono_Posix__SockaddrHeader* address, int flags)
+{
+ int r;
+
+ ALLOC_SOCKADDR
+
+ r = accept4 (socket, addr, &addrlen, flags);
+
+ if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) {
+ close (r);
+ r = -1;
+ }
+
+ if (need_free)
+ free (addr);
+
+ return r;
+}
+#endif
+
+int
+Mono_Posix_Syscall_getpeername (int socket, struct Mono_Posix__SockaddrHeader* address)
+{
+ int r;
+
+ ALLOC_SOCKADDR
+
+ r = getpeername (socket, addr, &addrlen);
+
+ if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0)
+ r = -1;
+
+ if (need_free)
+ free (addr);
+
+ return r;
+}
+
+int
+Mono_Posix_Syscall_getsockname (int socket, struct Mono_Posix__SockaddrHeader* address)
+{
+ int r;
+
+ ALLOC_SOCKADDR
+
+ r = getsockname (socket, addr, &addrlen);
+
+ if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0)
+ r = -1;
+
+ if (need_free)
+ free (addr);
+
+ return r;
+}
+