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; | |
74 | ||
75 | /* Initialize sockaddr for bind */ | |
76 | sin6ptr->sin6_family = family; | |
77 | sin6ptr->sin6_addr = in6addr_any; | |
78 | sin6ptr->sin6_port = htons(*port); | |
79 | ||
80 | socklen = sizeof(*sin6ptr); | |
81 | ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen); | |
82 | if (ret == -1) { | |
83 | goto cleanup; | |
84 | } | |
85 | ||
86 | ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen); | |
87 | if (ret == -1) { | |
88 | goto cleanup; | |
89 | } | |
90 | *port = ntohs(sin6ptr->sin6_port); | |
91 | } else { | |
92 | struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr; | |
93 | ||
94 | /* Initialize sockaddr for bind */ | |
95 | sinptr->sin_family = family; | |
96 | sinptr->sin_addr.s_addr = INADDR_ANY; | |
97 | sinptr->sin_port = htons(*port); | |
98 | ||
99 | socklen = sizeof(*sinptr); | |
100 | ret = bind(server_fd, (struct sockaddr *)sinptr, socklen); | |
101 | if (ret == -1) { | |
102 | goto cleanup; | |
103 | } | |
104 | ||
105 | ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen); | |
106 | if (ret == -1) { | |
107 | goto cleanup; | |
108 | } | |
109 | *port = ntohs(sinptr->sin_port); | |
110 | } | |
111 | return server_fd; | |
112 | ||
113 | cleanup: | |
114 | ret = SOCKET_GET_ERROR(); | |
115 | if (server_fd != -1) { | |
116 | closesocket(server_fd); | |
117 | } | |
118 | SOCKET_SET_ERROR(ret); | |
119 | return -1; | |
120 | } | |
121 | ||
122 | unsigned char * | |
123 | netutils_get_address(void *sockaddr, int *length) | |
124 | { | |
125 | unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 }; | |
126 | struct sockaddr *address = sockaddr; | |
127 | ||
128 | assert(address); | |
129 | assert(length); | |
130 | ||
131 | if (address->sa_family == AF_INET) { | |
132 | struct sockaddr_in *sin; | |
133 | ||
134 | sin = (struct sockaddr_in *)address; | |
135 | *length = sizeof(sin->sin_addr.s_addr); | |
136 | return (unsigned char *)&sin->sin_addr.s_addr; | |
137 | } else if (address->sa_family == AF_INET6) { | |
138 | struct sockaddr_in6 *sin6; | |
139 | ||
140 | sin6 = (struct sockaddr_in6 *)address; | |
141 | if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) { | |
142 | /* Actually an embedded IPv4 address */ | |
143 | *length = sizeof(sin6->sin6_addr.s6_addr)-12; | |
144 | return (sin6->sin6_addr.s6_addr+12); | |
145 | } | |
146 | *length = sizeof(sin6->sin6_addr.s6_addr); | |
147 | return sin6->sin6_addr.s6_addr; | |
148 | } | |
149 | ||
150 | *length = 0; | |
151 | return NULL; | |
152 | } | |
153 |