Commit | Line | Data |
---|---|---|
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 | ||
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; | |
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 | ||
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 | ||
ba0970e1 JVH |
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) { | |
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 | } |