Move src to src/lib, include to src/include, test to src/test.
[deb_shairplay.git] / src / lib / netutils.c
diff --git a/src/lib/netutils.c b/src/lib/netutils.c
new file mode 100644 (file)
index 0000000..255822b
--- /dev/null
@@ -0,0 +1,139 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "compat.h"
+
+int
+netutils_init()
+{
+#ifdef WIN32
+       WORD wVersionRequested;
+       WSADATA wsaData;
+       int ret;
+
+       wVersionRequested = MAKEWORD(2, 2);
+       ret = WSAStartup(wVersionRequested, &wsaData);
+       if (ret) {
+               return -1;
+       }
+
+       if (LOBYTE(wsaData.wVersion) != 2 ||
+           HIBYTE(wsaData.wVersion) != 2) {
+               /* Version mismatch, requested version not found */
+               return -1;
+       }
+#endif
+       return 0;
+}
+
+void
+netutils_cleanup()
+{
+#ifdef WIN32
+       WSACleanup();
+#endif
+}
+
+int
+netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
+{
+       int family = use_ipv6 ? AF_INET6 : AF_INET;
+       int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
+       int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
+
+       struct sockaddr_storage saddr;
+       socklen_t socklen;
+       int server_fd;
+       int ret;
+
+       assert(port);
+
+       server_fd = socket(family, type, proto);
+       if (server_fd == -1) {
+               goto cleanup;
+       }
+
+       memset(&saddr, 0, sizeof(saddr));
+       if (use_ipv6) {
+               struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
+
+               /* Initialize sockaddr for bind */
+               sin6ptr->sin6_family = family;
+               sin6ptr->sin6_addr = in6addr_any;
+               sin6ptr->sin6_port = htons(*port);
+
+               socklen = sizeof(*sin6ptr);
+               ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
+               if (ret == -1) {
+                       goto cleanup;
+               }
+
+               ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
+               if (ret == -1) {
+                       goto cleanup;
+               }
+               *port = ntohs(sin6ptr->sin6_port);
+       } else {
+               struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
+
+               /* Initialize sockaddr for bind */
+               sinptr->sin_family = family;
+               sinptr->sin_addr.s_addr = INADDR_ANY;
+               sinptr->sin_port = htons(*port);
+
+               socklen = sizeof(*sinptr);
+               ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
+               if (ret == -1) {
+                       goto cleanup;
+               }
+
+               ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
+               if (ret == -1) {
+                       goto cleanup;
+               }
+               *port = ntohs(sinptr->sin_port);
+       }
+       return server_fd;
+
+cleanup:
+       ret = SOCKET_GET_ERROR();
+       if (server_fd != -1) {
+               closesocket(server_fd);
+       }
+       SOCKET_SET_ERROR(ret);
+       return -1;
+}
+
+unsigned char *
+netutils_get_address(void *sockaddr, int *length)
+{
+       unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
+       struct sockaddr *address = sockaddr;
+
+       assert(address);
+       assert(length);
+
+       if (address->sa_family == AF_INET) {
+               struct sockaddr_in *sin;
+
+               sin = (struct sockaddr_in *)address;
+               *length = sizeof(sin->sin_addr.s_addr);
+               return (unsigned char *)&sin->sin_addr.s_addr;
+       } else if (address->sa_family == AF_INET6) {
+               struct sockaddr_in6 *sin6;
+
+               sin6 = (struct sockaddr_in6 *)address;
+               if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
+                       /* Actually an embedded IPv4 address */
+                       *length = sizeof(sin6->sin6_addr.s6_addr)-12;
+                       return (sin6->sin6_addr.s6_addr+12);
+               }
+               *length = sizeof(sin6->sin6_addr.s6_addr);
+               return sin6->sin6_addr.s6_addr;
+       }
+
+       *length = 0;
+       return NULL;
+}
+