IPV6_V6ONLY set by default on win32
[deb_shairplay.git] / src / lib / netutils.c
1 /**
2 * Copyright (C) 2011-2012 Juho Vähä-Herttua
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 */
14
15 #include <stdlib.h>
16 #include <string.h>
17 #include <assert.h>
18
19 #include "compat.h"
20
21 int
22 netutils_init()
23 {
24 #ifdef WIN32
25 WORD wVersionRequested;
26 WSADATA wsaData;
27 int ret;
28
29 wVersionRequested = MAKEWORD(2, 2);
30 ret = WSAStartup(wVersionRequested, &wsaData);
31 if (ret) {
32 return -1;
33 }
34
35 if (LOBYTE(wsaData.wVersion) != 2 ||
36 HIBYTE(wsaData.wVersion) != 2) {
37 /* Version mismatch, requested version not found */
38 return -1;
39 }
40 #endif
41 return 0;
42 }
43
44 void
45 netutils_cleanup()
46 {
47 #ifdef WIN32
48 WSACleanup();
49 #endif
50 }
51
52 int
53 netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
54 {
55 int family = use_ipv6 ? AF_INET6 : AF_INET;
56 int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
57 int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
58
59 struct sockaddr_storage saddr;
60 socklen_t socklen;
61 int server_fd;
62 int ret;
63
64 assert(port);
65
66 server_fd = socket(family, type, proto);
67 if (server_fd == -1) {
68 goto cleanup;
69 }
70
71 memset(&saddr, 0, sizeof(saddr));
72 if (use_ipv6) {
73 struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
74 int v6only = 1;
75
76 /* Initialize sockaddr for bind */
77 sin6ptr->sin6_family = family;
78 sin6ptr->sin6_addr = in6addr_any;
79 sin6ptr->sin6_port = htons(*port);
80
81 #ifndef WIN32
82 /* Make sure we only listen to IPv6 addresses */
83 setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
84 (char *) &v6only, sizeof(v6only));
85 #endif
86
87 socklen = sizeof(*sin6ptr);
88 ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
89 if (ret == -1) {
90 goto cleanup;
91 }
92
93 ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
94 if (ret == -1) {
95 goto cleanup;
96 }
97 *port = ntohs(sin6ptr->sin6_port);
98 } else {
99 struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
100
101 /* Initialize sockaddr for bind */
102 sinptr->sin_family = family;
103 sinptr->sin_addr.s_addr = INADDR_ANY;
104 sinptr->sin_port = htons(*port);
105
106 socklen = sizeof(*sinptr);
107 ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
108 if (ret == -1) {
109 goto cleanup;
110 }
111
112 ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
113 if (ret == -1) {
114 goto cleanup;
115 }
116 *port = ntohs(sinptr->sin_port);
117 }
118 return server_fd;
119
120 cleanup:
121 ret = SOCKET_GET_ERROR();
122 if (server_fd != -1) {
123 closesocket(server_fd);
124 }
125 SOCKET_SET_ERROR(ret);
126 return -1;
127 }
128
129 unsigned char *
130 netutils_get_address(void *sockaddr, int *length)
131 {
132 unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
133 struct sockaddr *address = sockaddr;
134
135 assert(address);
136 assert(length);
137
138 if (address->sa_family == AF_INET) {
139 struct sockaddr_in *sin;
140
141 sin = (struct sockaddr_in *)address;
142 *length = sizeof(sin->sin_addr.s_addr);
143 return (unsigned char *)&sin->sin_addr.s_addr;
144 } else if (address->sa_family == AF_INET6) {
145 struct sockaddr_in6 *sin6;
146
147 sin6 = (struct sockaddr_in6 *)address;
148 if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
149 /* Actually an embedded IPv4 address */
150 *length = sizeof(sin6->sin6_addr.s6_addr)-12;
151 return (sin6->sin6_addr.s6_addr+12);
152 }
153 *length = sizeof(sin6->sin6_addr.s6_addr);
154 return sin6->sin6_addr.s6_addr;
155 }
156
157 *length = 0;
158 return NULL;
159 }
160
161 int
162 netutils_parse_address(int family, const char *src, void *dst, int dstlen)
163 {
164 struct addrinfo *result;
165 struct addrinfo *ptr;
166 struct addrinfo hints;
167 int length;
168 int ret;
169
170 if (family != AF_INET && family != AF_INET6) {
171 return -1;
172 }
173 if (!src || !dst) {
174 return -1;
175 }
176
177 memset(&hints, 0, sizeof(hints));
178 hints.ai_family = family;
179 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
180
181 ret = getaddrinfo(src, NULL, &hints, &result);
182 if (ret != 0) {
183 return -1;
184 }
185
186 length = -1;
187 for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
188 if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
189 memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
190 length = ptr->ai_addrlen;
191 break;
192 }
193 }
194 freeaddrinfo(result);
195 return length;
196 }