Attempt to fix the IPv4/IPv6 problems once and for all
[deb_shairplay.git] / src / lib / netutils.c
CommitLineData
23e7e3ae
JVH
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
2340bcd3
JVH
15#include <stdlib.h>
16#include <string.h>
17#include <assert.h>
18
19#include "compat.h"
20
21int
22netutils_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
44void
45netutils_cleanup()
46{
47#ifdef WIN32
48 WSACleanup();
49#endif
50}
51
52int
53netutils_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;
870f342f 74 int v6only = 1;
2340bcd3
JVH
75
76 /* Initialize sockaddr for bind */
77 sin6ptr->sin6_family = family;
78 sin6ptr->sin6_addr = in6addr_any;
79 sin6ptr->sin6_port = htons(*port);
80
870f342f
JVH
81 /* Make sure we only listen to IPv6 addresses */
82 setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
83 (char *) &v6only, sizeof(v6only));
e161255a 84
2340bcd3
JVH
85 socklen = sizeof(*sin6ptr);
86 ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
87 if (ret == -1) {
88 goto cleanup;
89 }
90
91 ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
92 if (ret == -1) {
93 goto cleanup;
94 }
95 *port = ntohs(sin6ptr->sin6_port);
96 } else {
97 struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
98
99 /* Initialize sockaddr for bind */
100 sinptr->sin_family = family;
101 sinptr->sin_addr.s_addr = INADDR_ANY;
102 sinptr->sin_port = htons(*port);
103
104 socklen = sizeof(*sinptr);
105 ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
106 if (ret == -1) {
107 goto cleanup;
108 }
109
110 ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
111 if (ret == -1) {
112 goto cleanup;
113 }
114 *port = ntohs(sinptr->sin_port);
115 }
116 return server_fd;
117
118cleanup:
119 ret = SOCKET_GET_ERROR();
120 if (server_fd != -1) {
121 closesocket(server_fd);
122 }
123 SOCKET_SET_ERROR(ret);
124 return -1;
125}
126
127unsigned char *
128netutils_get_address(void *sockaddr, int *length)
129{
130 unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
131 struct sockaddr *address = sockaddr;
132
133 assert(address);
134 assert(length);
135
136 if (address->sa_family == AF_INET) {
137 struct sockaddr_in *sin;
138
139 sin = (struct sockaddr_in *)address;
140 *length = sizeof(sin->sin_addr.s_addr);
141 return (unsigned char *)&sin->sin_addr.s_addr;
142 } else if (address->sa_family == AF_INET6) {
143 struct sockaddr_in6 *sin6;
144
145 sin6 = (struct sockaddr_in6 *)address;
146 if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
147 /* Actually an embedded IPv4 address */
148 *length = sizeof(sin6->sin6_addr.s6_addr)-12;
149 return (sin6->sin6_addr.s6_addr+12);
150 }
151 *length = sizeof(sin6->sin6_addr.s6_addr);
152 return sin6->sin6_addr.s6_addr;
153 }
154
155 *length = 0;
156 return NULL;
157}
158
ba0970e1
JVH
159int
160netutils_parse_address(int family, const char *src, void *dst, int dstlen)
161{
162 struct addrinfo *result;
163 struct addrinfo *ptr;
164 struct addrinfo hints;
165 int length;
166 int ret;
167
168 if (family != AF_INET && family != AF_INET6) {
169 return -1;
170 }
171 if (!src || !dst) {
172 return -1;
173 }
174
175 memset(&hints, 0, sizeof(hints));
176 hints.ai_family = family;
177 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
178
179 ret = getaddrinfo(src, NULL, &hints, &result);
180 if (ret != 0) {
181 return -1;
182 }
183
184 length = -1;
185 for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
566c9bf8 186 if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
ba0970e1
JVH
187 memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
188 length = ptr->ai_addrlen;
189 break;
190 }
191 }
192 freeaddrinfo(result);
193 return length;
194}