180d32eed7f6b510880a9a6b1fe4c75351536d74
[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 /* Make sure we only listen to IPv6 addresses */
82 setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
83 (char *) &v6only, sizeof(v6only));
84
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
118 cleanup:
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
127 unsigned char *
128 netutils_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
159 int
160 netutils_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) {
186 if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
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 }