Attempt to make a temporary fix for Vista/Win7
[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 = 0;
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 /* Make sure we also listen to IPv4 addresses */
82 ret = setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
83 (char *) &v6only, sizeof(v6only));
84 if (ret == -1) {
85 goto cleanup;
86 }
87
88 socklen = sizeof(*sin6ptr);
89 ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
90 if (ret == -1) {
91 goto cleanup;
92 }
93
94 ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
95 if (ret == -1) {
96 goto cleanup;
97 }
98 *port = ntohs(sin6ptr->sin6_port);
99 } else {
100 struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
101
102 /* Initialize sockaddr for bind */
103 sinptr->sin_family = family;
104 sinptr->sin_addr.s_addr = INADDR_ANY;
105 sinptr->sin_port = htons(*port);
106
107 socklen = sizeof(*sinptr);
108 ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
109 if (ret == -1) {
110 goto cleanup;
111 }
112
113 ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
114 if (ret == -1) {
115 goto cleanup;
116 }
117 *port = ntohs(sinptr->sin_port);
118 }
119 return server_fd;
120
121 cleanup:
122 ret = SOCKET_GET_ERROR();
123 if (server_fd != -1) {
124 closesocket(server_fd);
125 }
126 SOCKET_SET_ERROR(ret);
127 return -1;
128 }
129
130 unsigned char *
131 netutils_get_address(void *sockaddr, int *length)
132 {
133 unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
134 struct sockaddr *address = sockaddr;
135
136 assert(address);
137 assert(length);
138
139 if (address->sa_family == AF_INET) {
140 struct sockaddr_in *sin;
141
142 sin = (struct sockaddr_in *)address;
143 *length = sizeof(sin->sin_addr.s_addr);
144 return (unsigned char *)&sin->sin_addr.s_addr;
145 } else if (address->sa_family == AF_INET6) {
146 struct sockaddr_in6 *sin6;
147
148 sin6 = (struct sockaddr_in6 *)address;
149 if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
150 /* Actually an embedded IPv4 address */
151 *length = sizeof(sin6->sin6_addr.s6_addr)-12;
152 return (sin6->sin6_addr.s6_addr+12);
153 }
154 *length = sizeof(sin6->sin6_addr.s6_addr);
155 return sin6->sin6_addr.s6_addr;
156 }
157
158 *length = 0;
159 return NULL;
160 }
161
162 int
163 netutils_parse_address(int family, const char *src, void *dst, int dstlen)
164 {
165 struct addrinfo *result;
166 struct addrinfo *ptr;
167 struct addrinfo hints;
168 int length;
169 int ret;
170
171 if (family != AF_INET && family != AF_INET6) {
172 return -1;
173 }
174 if (!src || !dst) {
175 return -1;
176 }
177
178 memset(&hints, 0, sizeof(hints));
179 hints.ai_family = family;
180 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
181
182 ret = getaddrinfo(src, NULL, &hints, &result);
183 if (ret != 0) {
184 return -1;
185 }
186
187 length = -1;
188 for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
189 if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
190 memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
191 length = ptr->ai_addrlen;
192 break;
193 }
194 }
195 freeaddrinfo(result);
196 return length;
197 }